【问题标题】:Forcing to download a file using PHP强制使用 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可以吗?
  • 你可以在这里找到另一个好的答案:stackoverflow.com/questions/10934022/…

标签: php http download


【解决方案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后,只需将readfile($filePath)替换为header('X-SENDFILE: ' . $filePath),网络服务器将负责文件服务,这比使用PHP readfile需要更少的资源。

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

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

          【讨论】:

          猜你喜欢
          • 2010-11-03
          • 1970-01-01
          • 2012-01-19
          • 2011-11-08
          • 2012-11-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多