在Nginx中使用X-Sendfile頭提升PHP文件下載的性能(針對大文件下載)
很多時候用戶需要從網(wǎng)站下載文件,如果文件是可以通過一個固定鏈接公開獲取的,那么我們只需將文件存放到 webroot下的目錄里就好。但大多數(shù)情況下,我們需要做權(quán)限控制,例如下載 PDF 賬單,又例如下載網(wǎng)盤里的檔案。這時,我們通常借助于腳本代碼來實現(xiàn),而這無疑會增加服務(wù)器的負(fù)擔(dān)。
例如下面的代碼:
<?php
// 用戶身份認(rèn)證,若驗證失敗跳轉(zhuǎn)
authenticate();
// 獲取需要下載的文件,若文件不存在跳轉(zhuǎn)
$file = determine_file();
// 讀取文件內(nèi)容
$content=file_get_contents($file);
// 發(fā)送合適的 HTTP 頭
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header("Content-Length: ". filesize($file));
echo $content; // 或者 readfile($file);
?>
一、這樣做有什么問題?
這樣做意味著我們的程序需要將文件內(nèi)容從磁盤經(jīng)過一個固定的 buffer 去循環(huán)讀取到內(nèi)存,再發(fā)送給前端 web 服務(wù)器,最后才到達(dá)用戶。當(dāng)需要下載的文件很大的時候,這種方式將消耗大量內(nèi)存,甚至引發(fā) php 進(jìn)程超時或崩潰。Cache 也很頭疼,更不用說中斷重連的情況了。
一個理想的解決方式應(yīng)該是,由 php 程序進(jìn)行權(quán)限檢查等邏輯判斷,一切通過后,讓前臺的 web 服務(wù)器直接將文件發(fā)送給用戶——像 Nginx 這樣的前臺更善于處理靜態(tài)文件。這樣一來 php 腳本就不會被 I/O 阻塞了。
二、什么是 X-Sendfile?
X-Sendfile 是一種將文件下載請求由后端應(yīng)用轉(zhuǎn)交給前端 web 服務(wù)器處理的機制,它可以消除后端程序既要讀文件又要處理發(fā)送的壓力,從而顯著提高服務(wù)器效率,特別是處理大文件下載的情形下。
X-Sendfile 通過一個特定的 HTTP header 來實現(xiàn):在 X-Sendfile 頭中指定一個文件的地址來通告前端 web 服務(wù)器。當(dāng) web 服務(wù)器檢測到后端發(fā)送的這個 header 后,它將忽略后端的其他輸出,而使用自身的組件(包括 緩存頭 和 斷點重連 等優(yōu)化)機制將文件發(fā)送給用戶。
不過,在使用 X-Sendfile 之前,我們必須明白這并不是一個標(biāo)準(zhǔn)特性,在默認(rèn)情況下它是被大多數(shù) web 服務(wù)器禁用的。而不同的 web 服務(wù)器的實現(xiàn)也不一樣,包括規(guī)定了不同的 X-Sendfile 頭格式。如果配置失當(dāng),用戶可能下載到 0 字節(jié)的文件。
使用 X-Sendfile 將允許下載非 web 目錄中的文件(例如/root/),即使文件在 .htaccess 保護(hù)下禁止訪問,也會被下載。
不同的 web 服務(wù)器實現(xiàn)了不同的 HTTP 頭
| SENDFILE 頭 | 使用的 WEB 器 |
|---|---|
| X-Sendfile | Apache, Lighttpd v1.5, Cherokee |
| X-LIGHTTPD-send-file | Lighttpd v1.4 |
| X-Accel-Redirect | Nginx, Cherokee |
使用 X-SendFile 的缺點是你失去了對文件傳輸機制的控制。例如如果你希望在完成文件下載后執(zhí)行某些操作,比如只允許用戶下載文件一次,這個 X-Sendfile 是沒法做到的,因為后臺的 php 腳本并不知道下載是否成功。
三、怎樣使用?
Apache 請參考mod_xsendfile模塊。下面我介紹 Nginx 的用法。
Nginx 默認(rèn)支持該特性,不需要加載額外的模塊。只是實現(xiàn)有些不同,需要發(fā)送的 HTTP 頭為 X-Accel-Redirect。另外,需要在配置文件中做以下設(shè)定
location /protected/ {
internal;
root /some/path;
}
表示這個路徑只能在 Nginx 內(nèi)部訪問,不能用瀏覽器直接訪問防止未授權(quán)的下載。
于是 PHP 發(fā)送 X-Accel-Redirect 給 Nginx:
]<?php
$filePath = '/protected/iso.img';
header('Content-type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
//讓Xsendfile發(fā)送文件
header('X-Accel-Redirect: '.$filePath);
?>
這樣用戶就會下載到 /some/path/protected/iso.img 這個路徑下的文件。
如果你想發(fā)送的是 /some/path/iso.img 文件,那么 Nginx 配置應(yīng)該是
location /protected/ {
internal;
alias /some/path/; # 注意最後的斜杠
}
相關(guān)文章
Nginx 反向代理緩存(proxy_cache)的實現(xiàn)
Nginx的緩存加速功能是由proxy_cache和fastcgi_cache兩個功能模塊完成,本文主要介紹了Nginx 反向代理緩存(proxy_cache)的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2024-05-05
Nginx?Rewrit實現(xiàn)網(wǎng)頁跳轉(zhuǎn)功能詳細(xì)步驟
Rewrite主要實現(xiàn)url地址重寫,以及重定向,就是把傳入web的請求重定向到其他url的過程,這篇文章主要介紹了Nginx?Rewrit實現(xiàn)網(wǎng)頁跳轉(zhuǎn)功能詳細(xì)步驟,需要的朋友可以參考下2024-02-02
Nginx訪問控制與參數(shù)調(diào)優(yōu)的方法
這篇文章主要介紹了Nginx訪問控制與參數(shù)調(diào)優(yōu)的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03

