1. 程式人生 > >php檔案下載的一些淺談

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); 中的第二個引數一定要標識檔案字尾名,第一個引數為檔案所在路徑。