使用Nginx的X-Sendfile機制提升PHP檔案上傳效能

來源:互聯網
上載者:User
使用Nginx的X-Sendfile機制提升PHP檔案下載效能
很多時候使用者需要從網站下載檔案,如果檔案是可以通過一個固定連結公開擷取的,那麼我們只需將檔案存放到 webroot 下的目錄裡就好。但大多數情況下,我們需要做許可權控制,例如下載 PDF 賬單,又例如下載網盤裡的檔案。這時,我們通常藉助於指令碼代碼來實現,而這無疑會增加伺服器的負擔。


例如下面的代碼:

// 使用者身份認證,若驗證失敗跳轉
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 內部訪問,不能用瀏覽器直接存取防止未授權的下載。

於是 PHP 發送 X-Accel-Redirect 給 Nginx:

$filePath = '/protected/iso.img';
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
//讓Xsendfile傳送檔案
header('X-Accel-Redirect: '.$filePath);
?>這樣使用者就會下載到 /some/path/protected/iso.img 這個路徑下的檔案。

如果你想發送的是 /some/path/iso.img 檔案,那麼 Nginx 配置應該是

location /protected/ {
internal;
alias /some/path/; # 注意最後的斜杠


作者:lovelucy
  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.