2015-02-02

Content-Encoding: HTTP deflate在IE的issue

是的,又是萬惡的IE。

這是我之前處理壓縮檔在IE下載與解壓縮的過程中,所遇到的問題。



在PHP中,常用壓縮函式代表的實際壓縮格式如下:

gzencode() == gzip
gzcompress() == zlib (aka. HTTP deflate)
gzdeflate()  == *raw* deflate encoding

格式差異如下:
deflate(RFC1951):一種壓縮算法,使用LZ77 & haffman編碼;
zlib(RFC1950):一種格式,對deflate進行了簡單的封裝;
gzip(RFC1952):一種格式,也是對deflate進行的封裝.

因此要注意的是,gzdeflate跟HTTP deflate(zlib)是不一樣的!要用gzcompress才對。

IE或其它瀏覽器支援gzip壓縮是沒啥問題的,有無標頭尾都可正常開啟;
但IE6, 7, 8一遇到deflate就不正常了,必須將zlib去頭(2 bytes)去尾(4 bytes),這樣IE才看得懂,
而且還得送出必要的header給IE才能force download,真的是問題多多。

總而言之,
解HTTP的content-encoding如下:
deflate: gzinflate( substr($HTTP_RAW_DATA,2,-4) ) 或 gzuncompress($HTTP_RAW_DATA)
gzip: gzinflate( substr($HTTP_RAW_DATA,10,-8) ) 或 gzdecode($HTTP_RAW_DATA)

壓縮HTTP的content-encoding如下:
deflate: gzcompress($DATA) (但IE6~8傳送壓縮檔內容要去頭2尾4)
gzip: gzencode($DATA)

IE讀deflate解法(去頭尾轉成raw deflate):

1) 加header:

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');//別用force-download,也無須指定mime-type
header("Content-Encoding: deflate");//表示壓縮
header("Content-Length: " . filesize($file_path)-6); //去掉zlib頭尾(6 bytes)後的size
header("Content-Disposition: attachment; filename=" . $true_filename);

//以下三行修正IE
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Pragma: public");
header('Expires: 0');

2)讀檔

//防止再次壓縮
if(ini_get('zlib.output_compression')) ini_set('zlib.output_compression', 'Off');
set_time_limit(0);ob_flush(); ob_end_clean();

//修正IE,chunk download可支援大檔下載
if($fp = fopen($file_path, 'rb')){    
fseek($fp, 2);//去掉zlib頭(2 bytes), 尾可不去    
while(!feof($fp)){            
print(fread($fp, 1024*8));            
ob_flush();            
flush();            
ob_end_flush();    
}    
fclose($fp);
}

@ob_flush();@flush();@ob_end_flush();@ob_end_clean();

沒有留言:

張貼留言