如何下载文件?
方法一, 直接通过 nginx 下载静态文件
如果文件是保存在服务器上面的, 可以直接用 nginx 下载文件
比如说可以供用户下载 PDF 文件, 那么我的 nginx 配置可以是这样子的:
- location ~ /document/(.*)\.PDF$ {
- root /home/nemo/myfile;
- try_files /$uri 404;
- }
按照上面的配置, 当我请求 http://fbd.intelleeegooo.cc/document/test.pdf 的时候, 我服务器上的位于 /home/nemo/myfile/document/test.PDF 的这个文件就被下载了. 当找不到相应的文件的时候, 就会返回 404 .
方法二, 通过 PHP 读取文件并下载
但上面这种方式是所有人都可以下载 PDF 文件的, 假如说下载文件这个动作是与账号有关的, 比如说某用户只能下载某些文件, 那么就需要在 PHP 里面对用户账户进行处理并且下载相关文件.
看我在 index.PHP 里面这段示例代码, 这段代码的功能下载 test.txt 文件
- <?PHP
- $filePath = '/home/nemo/fun/testdownloadfile/test.txt';
- $fileName = 'test.txt';
- readfile($filePath);
比如说我开了一个 8764 端口, nginx 配置如下:
- server {
- listen 8764;
- server_name xx.xx.xx;
- ......
- ......
- ......
- location / {
- root /home/nemo/fun/testdownloadfile;
- fastcgi_pass 127.0.0.1:xxxx;
- fastcgi_index index.PHP;
- include fastcgi.conf;
- }
- }
配置文件里面的 fastcgi_pass 后面可以是 ip + 端口, 也可以是 unix_socket 的路径. 具体根据你安装的 PHP 的里面的 PHP-fpm.conf 的 listen 来决定.
我们用 command + option + i 快捷键打开浏览器的调试模式, 当我在浏览器里面请求 http://xx.xx.xx:8764/ 的时候, 结果是浏览器直接把 txt 文件的内容显示在了页面上.
看一下调试模式里面的这个请求, 它的 response header 如下:
可以看到它里面的 Content-Type 是 text/html , 表示是一个 HTML 文件, 所以浏览器就直接展示在页面上了.[关于常用的一些 Content-Type , 可以见本文最后]
那么我改一下代码, 在里面设置一下 header, 示例代码如下:
- <?PHP
- $filePath = '/home/nemo/fun/testdownloadfile/test.txt';
- $fileName = 'test.txt';
- header('Content-Disposition: attachment; filename=' . $fileName);
- readfile($filePath);
我在 Chrome 里面新建一个 tab 页输入 url http://fbd.intelleeegooo.cc/document/test.pdf 的时候, 成功下载了这个文件, 如下图所示:
但是我在 Safari 里面的时候, 下载下来的文件多了一个 HTML 后缀, 如下图所示
我再改下代码, 设置 Content-Type , 看如下示例代码:
- <?PHP
- $filePath = '/home/nemo/fun/testdownloadfile/test.txt';
- $fileName = 'test.txt';
- header('Content-Type: application/octet-stream;charset=utf-8');
- header('Content-Disposition: attachment; filename=' . $fileName);
- readfile($filePath);
这样改过之后, 在 Safari 里面下载的文件就是正常的了, 不带 HTML 后缀的.
2.2 在 PHP 里面读取并输出文件的几种方法
在设置完 header 信息之后, 下面几种方法都可以用来输出文件
file_get_contents(), 这个方法是把文件的内容以字符串的形式全部读取到内存里面. 当文件比较大的时候, 会超过内存限制
- $content = file_get_contents($filePath);
- echo $content;
file() , 将文件以行的形式全部读取到数组中. 当文件比较大的时候, 会超过内存限制
- $f = file($filePath);
- while(list($line, $content) = each($f)) { // $line 是 int 类型表示是第几行 (从 0 开始), $content 是字符串类型表示这一行的内容
- echo $content;
- }
readfile() , 读取文件并且写入到输出缓冲区. 这种方式可以输出大文件, 读取单个文件不会超出内存限制.
- ob_end_clean();
- readfile($filePath);
但是看官方手册上面的这段话 http://php.net/manual/en/function.readfile.php
readfile 自身不会导致任何内存问题. 如果出现内存不足的问题, 使用 ob_get_level() 确保输出缓存已经关闭.
但 readfile() 方法还是可以会引起内存耗尽
readfile 实际上还是需要采用 MMAP(如果支持), 或者是一个固定的 buffer 去循环读取文件, 直接输出.
fopen(), 这就类似于 C 语言里面的读取文件. fopen 每次可以指定读取某个块大小的内容, 可以读入大文件. 不会超过内存限制
- $file = @fopen($filePath,"rb");
- while(!feof($file)) {
- print(@fread($file, 1024*8));
- ob_flush();
- flush();
- }
2.3 内存限制
在 PHP 的配置文件 PHP.INI 里面, 有一个 memory_limit 这个设置项, 设置的是每个脚本可以分配的内存.
如下图所示, 我自己放宽了一点变成了 256M, 默认是 128M
正如上面所说, 读取大文件的时候, 可能会内存耗尽.
PHP 里面有 ini_set() 方法可以在脚本运行时保持新的值, 在脚本结束时恢复.
并不是 PHP.INI 里面的所有设置项都可以被修改, 所有可以被 ini_set() 修改的选项可以从 官方手册里面的这个清单 http://php.net/manual/zh/ini.list.php 知晓
有一种方法可以在执行的时候动态的修改脚本可以使用的内存大小, 而不一定非要修改 PHP.INI 文件, 毕竟 PHP.INI 是针对全局的.
在脚本里面动态的修改一些设置, 只对该脚本有效, 实际上并不真正地修改 PHP.INI 文件.
2.5 时间限制
一般情况下, 使用 PHP 下载文件的时候, 会加上一行 set_time_limit(0);, 表示不限制这个 PHP 脚本执行的时间
- <?PHP
- $filePath = '/home/nemo/fun/testdownloadfile/test.txt';
- $fileName = 'test.txt';
- set_time_limit(0);
- header('Content-Type: application/octet-stream;charset=utf-8');
- header('Content-Disposition: attachment; filename=' . $fileName);
- readfile($filePath);
看下 官方手册上 的解释
Content-Disposition 相关解释
在常规的 HTTP 应答中, Content-Disposition 消息头指示回复的内容该以何种形式展示, 是以内联的形式 (即网页或者页面的一部分), 还是以附件的形式下载并保存到本地
Content-Disposition 消息头最初是在 MIME 标准中定义的, HTTP 表单及 POST 请求只用到了其所有参数的一个子集. 只有 form-data 以及可选的 name 和 filename 三个参数可以应用在 HTTP 场景中
inline
inline 展示 txt 文件
看如下示例代码, 设置 inline 内联, 将上面的 test.txt 文件在浏览器里面展示
- <?PHP
- $filePath = '/home/nemo/fun/testdownloadfile/test.txt';
- $fileName = 'test.txt';
- header('Content-Disposition: inline; filename=' . $fileName);
- readfile($filePath);
常用的几种 Content-Type 类型
下面列一下常用的几种 Content-Type
text/HTML , 内容是 HTML 格式
text/plain , 内容是纯文本格式
image/gif , gif 图片格式
image/jpeg , jpg 图片格式
image/PNG , PNG 图片格式
multipart/form-data , 常见的 POST 数据提交的方式. 当需要上传文件时, 会用到这种类型
application/JSON , 消息主体是序列化后的 JSON 字符串
application/octet-stream , 二进制流数据. 一般在下载文件的时候比较常见
application/x-www-form-urlencoded , 浏览器的原生 form 表单, 提交的数据按照 key1=val1&key2=val2 的方式进行编码, key 和 val 都进行了 URL 转码
总结
来源: https://www.jb51.net/article/150497.htm