nginx sendfile:使用Nginx的X-Sendfile機制提升PHP檔案下載效能
來源:互聯網
上載者:User
很多時候使用者需要從網站下載檔案,如果檔案是可以通過一個固定連結公開擷取的,那麼我們只需將檔案存放到 webroot 下的目錄裡就好。但大多數情況下,我們需要做許可權控制,例如下載 PDF 賬單,又例如下載網盤裡的檔案。這時,我們通常藉助於指令碼代碼來實現,而這無疑會增加伺服器的負擔。
例如下面的代碼:
<?php
// 使用者身份認證,若驗證失敗跳轉
authenticate();
// 擷取需要下載的檔案,若檔案不存在跳轉
$file = determine_file();
// 讀取檔案內容
$content=file_get_contents($file);
// 發送合適的 HTTP 頭
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header("Content-Length: ". filesize($file));
echo $content; // 或者 readfile($file);
?>一、這樣做有什麼問題?
這樣做意味著我們的程式需要將檔案內容從磁碟經過一個固定的 buffer 去迴圈讀取到記憶體,再發送給前端 網頁伺服器,最後才到達使用者。當需要下載的檔案很大的時候,這種方式將消耗大量記憶體,甚至引發 php 進程逾時或崩潰。Cache 也很頭疼,更不用說中斷重連的情況了。
一個理想的解決方式應該是,由 php 程式進行許可權檢查等邏輯判斷,一切通過後,讓前台的 網頁伺服器直接將檔案發送給使用者——像 Nginx 這樣的前台更善於處理靜態檔案。這樣一來 php 指令碼就不會被 I/O 阻塞了。
二、什麼是 X-Sendfile?
X-Sendfile 是一種將檔案下載請求由後端應用轉交給前端 網頁伺服器處理的機制,它可以消除後端程式既要讀檔案又要處理髮送的壓力,從而顯著提高伺服器效率,特別是處理大檔案下載的情形下。
X-Sendfile 通過一個特定的 HTTP header 來實現:在 X-Sendfile 頭中指定一個檔案的地址來通告前端 網頁伺服器。當 網頁伺服器檢測到後端發送的這個 header 後,它將忽略後端的其他輸出,而使用自身的組件(包括 緩衝頭 和 斷點重連 等最佳化)機制將檔案發送給使用者。
不過,在使用 X-Sendfile 之前,我們必須明白這並不是一個標準特性,在預設情況下它是被大多數 網頁伺服器禁用的。而不同的 網頁伺服器的實現也不一樣,包括規定了不同的 X-Sendfile 頭格式。如果配置失當,使用者可能下載到 0 位元組的檔案。
使用 X-Sendfile 將允許下載非 web 目錄中的檔案(例如/root/),即使檔案在 .htaccess 保護下禁止訪問,也會被下載。
不同的 網頁伺服器實現了不同的 HTTP 頭 sendfile 頭 使用的 網頁伺服器
X-Sendfile Apache, Lighttpd v1.5, Cherokee
X-LIGHTTPD-send-file Lighttpd v1.4
X-Accel-Redirect Nginx, Cherokee
使用 X-SendFile 的缺點是你失去了對檔案傳輸機制的控制。例如如果你希望在完成檔案下載後執行某些操作,比如只允許使用者下載檔案一次,這個 X-Sendfile 是沒法做到的,因為背景 php 指令碼並不知道下載是否成功。
三、怎樣使用?
Apache 請參考mod_xsendfile模組。下面我介紹 Nginx 的用法。
Nginx 預設支援該特性,不需要載入額外的模組。只是實現有些不同,需要發送的 HTTP 頭為 X-Accel-Redirect。另外,需要在設定檔中做以下設定
location /protected/ {
internal;
root /some/path;
}internal 表示這個路徑只能在 Nginx 內部訪問,不能用瀏覽器直接存取防止未授權的下載。 本文連結http://www.cxybl.com/html/wlbc/Php/20130504/37467.html