php檔案下載的一些淺談
在web開發過程中,檔案的上傳下載是必須的,同時竟會經常出現一些無厘頭的bug,接下來講一些php檔案下載需要注意的地方。
(1)php直譯器配置檔案是必不可少的,在php.ini配置檔案中 開啟phpinfo.dll擴充套件
extension=php_fileinfo.dll 去掉前面的;即可
(2)如果經驗豐富的話,我們知道,一些簡單的檔案下載,如docx,rar等一些檔案格式的,由於它是不能被瀏覽器預設識別的MIME型別,因為在實現下載時只需要用<ahref="....../docx/xxx.docx"></a>連結地址指向檔案儲存路徑即可,瀏覽器會自動彈出開啟或下載的提示框,這樣客戶端便可以實現下載,但是對於圖片格式,如gif、jpg
還有視訊格式,如MP4、wmv等檔案格式的,當我們還是使用上面那種方式時,發現瀏覽器會直接開啟它,並不會提示下載,這是因為這些檔案型別是瀏覽器可是預設識別的。
這時,我們就需要通過另一種方式來實現了。
上述通過<a>實現的下載實際上是不安全的,因為將檔案路徑資訊暴露在了瀏覽器中。合適的做法應該是伺服器端通過header將頭資訊回傳給瀏覽器,該函式接受一個頭資訊作為引數。檔案下載需要傳送的頭資訊包括以下三部分,通過呼叫三次header()函式完成
header(‘Content-Type:imge/gif'); //傳送指定檔案MIME型別的頭資訊 header(‘Content-Disposition:attachment; filename=”test.gif”‘); //傳送描述檔案的頭資訊,附件和檔名 header(‘Content-Length:3390′); //傳送指定檔案大小的資訊,單位位元組
有了這三個頭資訊,瀏覽器就不會直接開啟檔案,而是讓瀏覽器以下載形式開啟檔案。接下來就需要將檔案內容輸出到瀏覽器,以便瀏覽器進行下載,使用php中的readfile()函式,可以檔案直接輸出。
(3)下面講一下tp框架中的Http.class.php中的download方法(個人感覺還是很好用的!)
public static function download($filename, $showname = '', $content = '', $expire = 180) { ob_end_clean(); if (is_file($filename)) { $length = filesize($filename); } elseif (is_file(UPLOAD_PATH . $filename)) { $filename = UPLOAD_PATH . $filename; $length = filesize($filename); } elseif ('' != $content) { $length = strlen($content); } else { E($filename . L('下載檔案不存在!')); } if (empty($showname)) { $showname = $filename; } $showname = basename($showname); if (!empty($filename)) { $finfo = new \finfo(FILEINFO_MIME); $type = $finfo->file($filename); } else { $type = "application/octet-stream"; } //傳送Http Header資訊 開始下載 header("Pragma: public"); header("Cache-control: max-age=" . $expire); //header('Cache-Control: no-store, no-cache, must-revalidate'); header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expire) . "GMT"); header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . "GMT"); header("Content-Disposition: attachment; filename=" . $showname); header("Content-Length: " . $length); header("Content-type: " . $type); header('Content-Encoding: none'); header("Content-Transfer-Encoding: binary"); if ('' == $content) { readfile($filename); } else { echo ($content); } exit(); }
前面的獲取檔案內容,設定下載附件名字,獲取檔案大小、型別在這裡不做過多介紹,這些大家都能看懂,主要說一下下面的幾個header()函式。
header("Pragma: public");
pragma是HTTP 1.0協議,通常在不快取情況下使用,通常為 pragma:no-cache 這兒設定public 應該是可以在任何地方快取
header("Cache-control: max-age=" . $expire);
上述是Cache-control可以設定的一些引數值,這裡設定的是 max-age:內容能被快取的時間,這個引數由呼叫時方法引數給定。
header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . "GMT");
客戶端通過瀏覽器訪問伺服器第一次請求時,伺服器端的返回狀態是200,內容是客戶端請求資源,同時還會有一個Last-Modified的屬性標記此檔案在服務期端最後被修改的時
間,當瀏覽器第二次訪問伺服器的時候,瀏覽器會發送一個If-Modified-Since 報頭,詢問該時間之後檔案是否有被修改過,如果沒有修改的話,伺服器會返回一個304狀態嗎,
內容為空,這樣就可以節省資料流量。
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expire) . "GMT");
設定瀏覽器快取時間,在瀏覽器中有快取區。這時當瀏覽器向同一url傳送請求時,瀏覽器有兩種處理方式:
1.直接在緩衝區內取資源:如果在伺服器端設定了expires,max-age時,瀏覽器不會在有效時期內再去向伺服器傳送請求,而是直接從快取區取資源
2.向瀏覽器傳送請求資源。這種情況發生在在頭部設定expires:no-cache時,或者是設定了 Expires,max-age但瀏覽器行為是 ' 重新整理' 或 '過載'時候。
header('Content-Encoding: none');
header("Content-Transfer-Encoding: binary");
http協議中的編碼協議和資料傳輸編碼協議,第一個none表示編碼內容預設,通常它有gzip,deflate,compress三種方式。第二個表示使用二進位制方式傳輸。
下面說一個使用http.class.php中的download方法過程中需要注意的:
(1)下載檔名最好不要用中文命名,經常會出現找不到下載檔案的問題。
(2)如果下載完成後,例如下載了一個視訊檔案,但是開啟時顯示無效檔案。這時下載過程中的錯誤,這時候嘗試在執行下載操作前,清空瀏覽器快取區。使用ob_end_clean()函式。這個問題在使用tp框架中的verify時,驗證碼圖片載入不出來也同樣適用。
(3)Http:download($filename,$showname); 中的第二個引數一定要標識檔案字尾名,第一個引數為檔案所在路徑。