[TOC]
# PHP實現
~~~
$pathToFile = 'myfile.csv';//文件絕對路徑
$downloadName = 'downloadFile.csv';//下載后的文件名
//輸入文件標簽
Header("Content-type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length: " . filesize($pathToFile));
Header("Content-Disposition: filename=" . $downloadName);
//輸出文件內容
$file = fopen($pathToFile, "r");
echo fread($file, filesize($pathToFile));
fclose($file);
//或
//readfile($pathToFile);
~~~
其中fread()與readfile()的區別可以參考
https://segmentfault.com/q/1010000000647226
但是有時候為了節省帶寬,避免瞬時流量過大而造成網絡堵塞,就要考慮下載限速的問題
# 下載文件限速
~~~
$pathToFile = 'myfile.csv';//文件絕對路徑
$downloadName = 'downloadFile.csv';//下載后的文件名
$download_rate = 30;// 設置下載速率(30 kb/s)
if (file_exists($pathToFile) && is_file($pathToFile)) {
header('Cache-control: private');// 發送 headers
header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($pathToFile));
header('Content-Disposition: filename=' . $downloadName);
flush();// 刷新內容
$file = fopen($pathToFile, "r");
while (!feof($file)) {
print fread($file, round($download_rate * 1024));// 發送當前部分文件給瀏覽者
flush();// flush 內容輸出到瀏覽器端
sleep(1);// 終端1秒后繼續
}
fclose($file);// 關閉文件流
} else {
abort(500, '文件' . $pathToFile . '不存在');
}
~~~
此時出現一個問題,當$download_rate>1kb時,文件正常下載;當$download_rate<1kb時,文件要等一會兒才下載,究其原因是因為buffer的問題。
* buffer是一個內存地址空間,Linux系統默認大小一般為4096(1kb),即一個內存頁。主要用于存儲速度不同步的設備或者優先級不同的設備之間傳辦理數據的區域。舉個例子,你打開文本編輯器編輯一個文件的時候,你每輸入一個字符,操作系統并不會立即把這個字符直接寫入到磁盤,而是先寫入到buffer,當寫滿了一個buffer的時候,才會把buffer中的數據寫入磁盤。同樣的道理,當執行echo,print的時候,輸出并沒有立即通過tcp傳給客戶端瀏覽器顯示,而是將數據寫入php buffer。php output_buffering機制,意味在tcp buffer之前,建立了一新的隊列,數據必須經過該隊列。當一個php buffer寫滿的時候,腳本進程會將php buffer中的輸出數據交給系統內核交由tcp傳給瀏覽器顯示。所以,數據會依次寫到這幾個地方echo/pring -> php buffer -> tcp buffer -> browser。資料:http://blog.csdn.net/superhosts/article/details/42292053
* 在沒有開啟緩存時,腳本輸出的內容都在服務器端處于等待輸出的狀態,flush()可以將等待輸出的內容立即發送到客戶端。
* 開啟緩存后,腳本輸出的內容存入了輸出緩存中,這時沒有處于等待輸出狀態的內容,你直接使用flush()不會向客戶端發出任何內容。而ob_flush()的作用就是將本來存在輸出緩存中的內容取出來,設置為等待輸出狀態,但不會直接發送到客戶端,這時你就需要先使用ob_flush()再使用flush(),客戶端才能立即獲得腳本的輸出。
* 以及這篇文章同樣講述了ob_flush()和flush()的區別http://www.laruence.com/2010/04/15/1414.html
但是這種方法將文件內容從磁盤經過一個固定的 buffer 去循環讀取到內存,再發送給前端 web 服務器,最后才到達用戶。當需要下載的文件很大的時候,這種方式將消耗大量內存,甚至引發 php 進程超時或崩潰,接下來就使用到X-Sendfile。
# X-Sendfile
X-Sendfile 是一種將文件下載請求由后端應用轉交給前端 web
服務器處理的機制,它可以消除后端程序既要讀文件又要處理發送的壓力,從而顯著提高服務器效率,特別是處理大文件下載的情形下。
我是用的nginx,所以apache請參考https://tn123.org/mod_xsendfile/
①首先在配置文件中添加
~~~
location /download/ {
internal;
root /some/path;//絕對路徑
}
~~~
internal 表示這個路徑只能在 Nginx 內部訪問,不能用瀏覽器直接訪問防止未授權的下載
注意添加在location / {...}的前面
這樣你在代碼中使用時,文件路徑就可以寫成“/download/myfile.csv”
②重啟Nginx,寫代碼
~~~
$pathToFile = 'myfile.csv';//文件絕對路徑
$downloadName = 'downloadFile.csv';//下載后的文件名
$download_rate = 30;// 設置下載速率(30 kb/s)
if (file_exists($pathToFile) && is_file($pathToFile)) {
return (new Response())->withHeaders([
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment;filename=' . $downloadName,
'X-Accel-Redirect' => $pathToFile,//讓Xsendfile發送文件
'X-Sendfile' => $pathToFile,
'X-Accel-Limit-Rate' => $download_rate,
]);
}else {
abort(500, '文件' . $pathToFile . '不存在');
}
~~~

- OAuth
- 簡介
- 步驟
- 單點登錄
- .user.ini
- 時間轉換為今天昨天前天幾天前
- 獲取ip接口
- 協程
- 概念
- yield-from && return-values
- 協程與阻塞的思考
- 中間件
- mysqli異步與php的協程
- 代碼片段
- pdo 執行的sql語句
- 二進制安全
- 捕捉異常中斷
- global
- 利用cookie模擬登陸
- 解析非正常json
- 簡單的對稱加密算法
- RSA 加密
- 過濾掉emoji表情
- 判斷遠程圖片是否存在
- 一分鐘限制請求100次
- 文件處理
- 多文件上傳
- 顯示所有文件
- 文件下載和上面顯示所有文件配合
- 文件的刪除,統計,存數組等
- 圖片處理
- 簡介
- 驗證碼
- 圖片等比縮放
- 批量添加水印
- beanstalkd
- 安裝
- 使用
- RabbitMQ
- 簡介
- debain安裝
- centos安裝
- 常用方法
- 入門
- 工作隊列
- 訂閱,發布
- 路由
- 主題
- 遠程調用RPC
- 消息中間件的選型
- .htaccess
- isset、empty、if區別以及0、‘’、null
- php各版本
- php7.2 不向后兼容的改動
- php中的各種坑
- php7改變
- php慢日志
- 郵件
- PHPMailer實現發郵件
- 驗證郵件地址真實性
- 文件下載
- FastCgi 與 PHP-fpm 之間的關系
- openssl 加解密
- 反射
- 鉤子方法
- 查找插件
- opcode
- opcache使用
- opcache優化
- 分布式一致性hash算法
- 概念
- 哈希算法好壞的四個定義
- php實現
- java實現
- 數組
- jwt
- jwt簡介
- 單點登錄
- phpize
- GeoIP擴展
- php無法獲得https網頁內容的解決方案
- homestead運行的腳本
- Unicode和Utf-8轉換
- php優化
- kafka
- fpm配置
- configure配置詳解