PHP 文件下载实现


各类文件下载举例


正文

方法一

第一种想到的办法就是直接链接:

但是,如果是图片、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

http://www.w3school.com.cn/php/php_file_open.asp

http://www.w3school.com.cn/php/php_ref_filesystem.asp


返回