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