正文
方法一
第一种想到的办法就是直接链接:
但是,如果是图片、pdf、html等浏览器可以识别的文件,那就会直接打开,而不是下载了。如下面:
方法二
接下来用redirect方式:
public function actionFileDown()
{
// 用Redirect方式
Header("Location: /upload/avatar.png");
}
点击后,看一下过程:
302,重定向。
可能存在的问题是地址是url网址,与文件地址相同,会有二义性,导致请求错误; 还有可能服务认为是网址,而不是文件地址,导致请求错误。
方法三
第三种方式:用文件处理API函数,fread(),可以避免文件地址暴露,又不会在页面上打开:
public function actionFileDown()
{
$file_name = "avatar.png"; // 下载文件名
$file_dir = "./upload/"; // 下载文件存放目录
// 检查文件是否存在
if (! file_exists ( $file_dir . $file_name ) ) {
echo "文件找不到";
exit ();
} else {
// 打开文件
$file = fopen ( $file_dir . $file_name, "r" );
//输入文件标签
Header ( "Content-type: application/octet-stream" );
Header ( "Accept-Ranges: bytes" );
Header ( "Accept-Length: " . filesize ( $file_dir . $file_name ) );
Header ( "Content-Disposition: attachment; filename=" . $file_name );
// 输出文件内容
// 读取文件内容并直接输出到浏览器
echo fread ( $file, filesize ( $file_dir . $file_name ) );
fclose ( $file );
exit ();
}
}
content-type的含义代表文件MIME类型是文件流格式。如果在Apache配置里面把文件的MIME类型设为application/octet-stream(如add application/octet-stream .xxx.rar),那么浏览器(客户端)就会知道,这是一个文件流格式的文件并提示用户下载。
Accept-Ranges是一个响应头标,它允许服务器指明将在给定的偏移和长度处,为资源组成部分的接受请求,该头标的值被理解为请求范围的度量单位。
Accept-Length是指定包含于请求或响应中数据的字节长度,例如,Content-Length:382。
Content-Disposition:attachment是用来告诉浏览器,文件是可以当做附件被下载,下载后的文件名称为$file_name该变量的值。
看一下一般html页面响应头:
请求头无变化:
方法四
第四种方式,用readfile()实现:
public function actionFileDown()
{
$filename = "./upload/avatar.png";
header('Content-Type:image/gif'); // 指定下载文件类型
header('Content-Disposition: attachment; filename="'.$filename.'"'); // 指定下载文件的描述
header('Content-Length:'.filesize($filename)); // 指定下载文件的大小
// 将文件内容读取出来并直接输出,以便下载
readfile( $filename );
}
看一下响应头:
方法五
第五种方式:
public function actionFileDown()
{
header("Content-type:text/html;charset=utf-8");
$file_name = "头像.png";
// 用以解决中文不能显示出来的问题
// $file_name = iconv("utf-8", "gb2312", $file_name); // 测试下,看服务器支持不
$file_sub_path = $_SERVER['DOCUMENT_ROOT'] . "/upload/";
$file_path = $file_sub_path . $file_name;
// 首先要判断给定的文件存在与否
if ( !file_exists( $file_path ) ) {
echo "没有该文件文件";
return ;
}
$fp = fopen($file_path, "r");
$file_size = filesize($file_path);
// 下载文件需要用到的头
Header("Content-type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length:".$file_size);
Header("Content-Disposition: attachment; filename=".$file_name);
$buffer = 1024;
$file_count = 0;
// 向浏览器返回数据
while ( !feof($fp) && $file_count < $file_size )
{
$file_con = fread($fp, $buffer);
$file_count += $buffer;
echo $file_con;
}
fclose($fp);
}
看一下响应头:
方法六
有可能用户请求服务网站,网站要远程去其他服务器获取文件,然后输出让用户下载,看一下代码:
public function actionFileDown($addr = "")
{
// 打开文件
$file = file_get_contents($addr);
$file_size = strlen($file);
//输入文件标签
Header ( "Content-type: application/octet-stream" );
Header ( "Accept-Ranges: bytes" );
Header ( "Accept-Length: " .$file_size );
Header ( "Content-Disposition: attachment; filename=" .time() .".xlsx" );
// 输出文件内容
echo $file;
exit();
}
Symfony包
symfony/http-foundation 包 Packagist: https://packagist.org/packages/symfony/http-foundation#v5.3.11 , 该包说明文档地址:https://symfony.com/doc/current/components/http_foundation.html#sending-the-response , symfony/symfony 包 github地址:https://github.com/symfony/symfony
HttpFoundation组件为HTTP规范定义了一个面向对象的层。
在PHP中,请求由一些全局变量($ GET、$POST、$FILES、$COOKIE、$SESSION,…)表示, 响应由一些函数(echo、header()、setcookie()、…)生成。
Symfony HttpFoundation组件将这些默认PHP全局变量和函数替换为面向对象的层。
下面实现 以文件流响应浏览器输出,内容是PHP回调函数,而不是字符串。
composer.json 文件引入Symfony包:
{
"require": {
"php": ">=5.6.0",
"symfony/http-foundation": "^5.3",
...
},
}
安装完成后,操作代码:
<?php
use Symfony\Component\HttpFoundation\StreamedResponse;
function export() {
$xlsx = new Spreadsheet(); // 可以是 phpoffice/phpspreadsheet 实现
// 数据填充操作...
$response = new StreamedResponse(
function () use ($xlsx) {
$writer = new Xlsx($xlsx);
$writer->save('php://output');
}
);
$response->headers->set('Content-Type', 'application/vnd.ms-excel');
$response->headers->set('Content-Disposition', 'attachment;filename="' . time() . '.xlsx' . '"');
$response->headers->set('Cache-Control', 'max-age=0');
$response->send();
exit();
}
参考资料
PHP 文件下载实现 http://blog.sina.com.cn/s/blog_6aba78b40102xbu8.html
https://www.cnblogs.com/wj-jingsi/p/5233819.html
http://www.jb51.net/article/53977.htm
http://www.jb51.net/article/40485.htm
http://www.jizhuomi.com/software/386.html
http://www.jb51.net/article/77067.htm