强制使用 PHP 下载文件

Posted

技术标签:

【中文标题】强制使用 PHP 下载文件【英文标题】:Forcing to download a file using PHP 【发布时间】:2010-11-30 17:40:30 【问题描述】:

我的服务器上有一个 CSV 文件。如果用户点击应该下载的链接,但它会在我的浏览器窗口中打开。

我的代码如下所示

<a href="files/csv/example/example.csv">
    Click here to download an example of the "CSV" file
</a>

这是一个普通的网络服务器,我的所有开发工作都在其中。

我尝试了类似的方法:

<a href="files/csv/example/csv.php">
    Click here to download an example of the "CSV" file
</a>

现在是我的csv.php 文件的内容:

header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');

现在我的问题是它正在下载,但不是我的 CSV 文件。它会创建一个新文件。

【问题讨论】:

这与 PHP 有什么关系? 你忘记输出文件内容了。 在下面更新了我的答案,为您的新问题提供了解决方案 不使用php可以吗? 你可以在这里找到另一个好的答案:***.com/questions/10934022/… 【参考方案1】:

配置您的服务器以发送媒体类型为application/octet-stream 的文件。

【讨论】:

【参考方案2】:

.htaccess 解决方案

要强制下载您服务器上的所有 CSV 文件,请添加您的 .htaccess 文件:

AddType application/octet-stream csv

PHP 解决方案

header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');
readfile("/path/to/yourfile.csv");

【讨论】:

为什么不把 Content-Type 行改为 Content-Type: application/octet-stream 呢?无论如何,当您覆盖它时,在 .htaccess 中执行它似乎有点多余。 那是问题的最初答案,然后问题发生了变化,所以第二部分就是答案。 这不适用于所有浏览器,所有文件类型。例如,它不适用于包含 PDF 文件和 csv 文件的最新(2013 年 8 月)版本的 Chrome。请参阅我的答案,了解如何以更可靠的方式使其工作。 很好的答案!我建议按照@Powerlord 的建议将'Content-Type: application/csv' 更改为'Content-Type: application/octet-stream'。这将是一个更全面的答案,并将帮助其他不仅对下载 CSV 感兴趣的人。只是一个建议!谢谢! 如果是zip 文件,你能告诉我怎么做吗??【参考方案3】:

这意味着您的浏览器可以处理这种文件类型。

如果您不喜欢它,最简单的方法是提供 ZIP 文件。每个人都可以处理 ZIP 文件,并且默认情况下可以下载。

【讨论】:

【参考方案4】:

这不能可靠地完成,因为由浏览器决定如何处理被要求检索的 URL。

您可以建议浏览器通过发送 Content-disposition 标头立即“保存到磁盘”:

header("Content-disposition: attachment");

我不确定各种浏览器对此的支持程度。另一种方法是发送一个 Content-type 的 application/octet-stream,但这是一种 hack(你基本上是在告诉浏览器“我没有告诉你这是什么类型的文件”,这取决于大多数浏览器随后会提供一个下载对话框)并据称会导致 Internet Explorer 出现问题。

在Web Authoring FAQ.了解更多信息

编辑 您已经切换到一个 PHP 文件来传递数据 - 这是设置 Content-disposition 标头所必需的(除非有一些神秘的 Apache 设置也可以做到这一点)。现在剩下要做的就是让该 PHP 文件读取 CSV 文件的内容并打印它们 - 标题中的 filename=example.csv 仅向客户端浏览器建议该文件使用什么名称,它实际上并不获取来自服务器上文件的数据。

【讨论】:

在所有 FF 版本、IE6 和 IE7 中使用“Content-disposition: attachment”对我们一直有效。 通常情况下,如果您为浏览器设置的下载文件的类型设置了默认操作,您只会遇到内容处置问题,否则浏览器通常会遵循建议(对于更现代的版本, ymmv 与旧/过时的浏览器版本)。【参考方案5】:

这是一个对浏览器更安全的解决方案:

    $fp = @fopen($yourfile, 'rb');

    if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))

    header('Content-Type: "application/octet-stream"');
    header('Content-Disposition: attachment; filename="yourname.file"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header("Content-Transfer-Encoding: binary");
    header('Pragma: public');
    header("Content-Length: ".filesize($yourfile));

else

    header('Content-Type: "application/octet-stream"');
    header('Content-Disposition: attachment; filename="yourname.file"');
    header("Content-Transfer-Encoding: binary");
    header('Expires: 0');
    header('Pragma: no-cache');
    header("Content-Length: ".filesize($yourfile));


fpassthru($fp);
fclose($fp);

【讨论】:

【参考方案6】:

不错的清洁解决方案:

<?php
    header('Content-Type: application/download');
    header('Content-Disposition: attachment; filename="example.csv"');
    header("Content-Length: " . filesize("example.csv"));

    $fp = fopen("example.csv", "r");
    fpassthru($fp);
    fclose($fp);
?>

【讨论】:

是什么让它变得干净整洁? 我只会使用来自文档If you just want to dump the contents of a file to the output buffer, without first modifying it or seeking to a particular offset, you may want to use the readfile(), which saves you the fopen() call. 的 readfile() 【参考方案7】:

此页面上的先前答案描述了如何使用 .htaccess 强制下载特定类型的所有文件。但是,该解决方案不适用于所有浏览器中的所有文件类型。这是一种更可靠的方式:

<FilesMatch "\.(?i:csv)$">
  ForceType application/octet-stream
  Header set Content-Disposition attachment
</FilesMatch>

您可能需要刷新浏览器缓存才能看到它正常工作。

【讨论】:

【参考方案8】:

或者您可以使用 html5 来执行此操作。只需使用

<a href="example.csv" download>download not open it</a>

【讨论】:

很遗憾,IE 或 Safari 不支持此功能:caniuse.com/#feat=download 添加 download 属性最好的部分是它会强制下载任何类型的文件。 Safari 终于支持了! 喜欢这个。为我节省了另一个晚上试图弄清楚如何使用那些该死的 php 下载标头。【参考方案9】:

如果你是用你的应用程序本身来做这件事......我希望这段代码会有所帮助。

HTML

在 href 中——您必须添加 download_file.php 以及您的 URL:

<a class="download" href="'/download_file.php?fileSource='+http://www.google.com/logo_small.png" target="_blank" title="YourTitle">

PHP

/* Here is the Download.php file to force download stuff */

<?php
    $fullPath = $_GET['fileSource'];
    if($fullPath) 
        $fsize = filesize($fullPath);
        $path_parts = pathinfo($fullPath);
        $ext = strtolower($path_parts["extension"]);

        switch ($ext) 
            case "pdf":
                header("Content-Disposition: attachment; filename=\"" . $path_parts["basename"]."\""); // Use 'attachment' to force a download
                header("Content-type: application/pdf"); // Add here more headers for diff. extensions
                break;

            default;
                header("Content-type: application/octet-stream");
                header("Content-Disposition: filename=\"" . $path_parts["basename"]."\"");
        

        if($fsize)  // Checking if file size exist
            header("Content-length: $fsize");
        
        readfile($fullPath);
        exit;
    
?>

【讨论】:

此模型可能会造成重大安全漏洞。攻击者可以使用它来下载您的源文件!谨慎使用!!!! 这看起来像是我多年前写的一些旧的下载脚本,你说得对,黑客可以尝试找到文件的位置。这种方式输入验证也很困难。几天前,我重新发布了一篇文章,其中包含第二个示例,该示例使用具有哈希 ID 的数据库来获取文件名。这不是一个完美的解决方案,但它指向正确的重定向:web-development-blog.com/archives/php-download-file-script 谈空字节注入? 尽管需要建议添加安全性,但这确实是 IMO 的最佳方法。 8 多年来,我一直使用类似的东西来允许访问者查看或下载 PDF 文件产品文档,现在大多数浏览器将“播放”(而不是下载)MP3、MP4 或其他媒体文件,这是最确保下载的可靠方法。它还可以轻松添加日志记录功能,因此您可以轻松查看有多少人下载、何时下载,甚至记录 IP 地址。太糟糕了,这没有得到更多的赞成票! @Olaf 我想我以前用过你的!【参考方案10】:

要强制下载,您可以使用大多数浏览器都支持的Content-Type: application/force-download 标头:

function downloadFile($filePath)

    header("Content-type: application/octet-stream");
    header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
    header('Content-Length: ' . filesize($filePath));
    readfile($filePath);

更好的方法

以这种方式下载文件并不是最好的主意,尤其是对于大文件。 PHP 将需要额外的 CPU/内存来读取和输出文件内容,并且在处理大文件时可能会达到时间/内存限制。

更好的方法是使用 PHP 对文件进行身份验证和授予访问权限,并且应该使用 X-SENDFILE 方法将实际文件服务委托给 Web 服务器(需要一些 Web 服务器配置):

X-SENDFILE 由 Lighttpd 原生支持:https://redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file Apache 需要 mod_xsendfile 模块:https://tn123.org/mod_xsendfile/ 在 Ubuntu 上可以安装:apt install libapache2-mod-xsendfile nginx 有一个类似的X-Accel-Redirect 标头:https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/

配置网络服务器处理X-SENDFILE后,只需将readfile($filePath)替换为header('X-SENDFILE: ' . $filePath),网络服务器将负责文件服务,这比使用PHP readfile需要更少的资源。

(对于 Nginx 使用 X-Accel-Redirect 标头而不是 X-SENDFILE

注意:如果你最终下载的是空文件,这意味着你没有配置你的 web 服务器来处理 X-SENDFILE 标头。检查上面的链接,了解如何正确配置您的网络服务器。

【讨论】:

避免使用Content-Type: application/force-download,阅读更多:***.com/a/10616753/260080 将 application/force-download 替换为 application/octet-stream

以上是关于强制使用 PHP 下载文件的主要内容,如果未能解决你的问题,请参考以下文章

强制使用 PHP 下载文件

如何在移动浏览器上使用 PHP 强制下载文件?

PHP如何强制下载文件

PHP强制下载.xlsx文件损坏

PHP强制下载.xlsx文件损坏

如何使用 PHP 在移动设备上强制下载音频 mp3