【问题标题】:How to force file download with PHP如何使用 PHP 强制文件下载
【发布时间】:2011-11-08 00:00:15
【问题描述】:

我想要求在用户使用 PHP 访问网页时下载文件。我认为它与file_get_contents有关,但不确定如何执行。

$url = "http://example.com/go.exe";

使用header(location) 下载文件后,它不会重定向到另一个页面。它只是停止。

【问题讨论】:

标签: php file download


【解决方案1】:

阅读有关内置 PHP 函数 readfile 的文档

$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary"); 
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\""); 
readfile($file_url); 

还要确保根据您的文件 application/zip、application/pdf 等添加正确的内容类型 - 但前提是您不想触发另存为对话框。

【讨论】:

  • 我的意思是,如果它不是 .exe 文件,而不仅仅是 .Else 这应该可以正常工作。
  • 别忘了刷新 ;-) ob_clean();冲洗(); /* 之前 */ readfile($file_url);
  • 如果你有一个 10GB 的大文件,php 会尝试加载整个文件吗?
  • exit() 应该在最后调用以避免任何潜在的问题(从经验说:-)
  • 我使用图像 http 路径作为路径源,但文件以二进制代码显示,而不是使用您的代码下载。不适合我。谢谢。
【解决方案2】:
<?php
$file = "http://example.com/go.exe"; 

header("Content-Description: File Transfer"); 
header("Content-Type: application/octet-stream"); 
header("Content-Disposition: attachment; filename=\"". basename($file) ."\""); 

readfile ($file);
exit(); 
?>

或者,当文件无法用浏览器打开时,您可以使用Location 标头:

<?php header("Location: http://example.com/go.exe"); ?>

【讨论】:

  • 标题中的文件名不应该是$file(包含http部分),而是一个有效的文件名。
  • 没有application/force-download媒体类型;请改用 application/octet-stream。
  • 效果很好!但是将 ' 添加到存储的文件名中。请使用:filename=".basename($file));
  • @harry4516 根据你的发现修改
  • 对我来说,如果是 png 图像,则无法正常工作。谢谢。
【解决方案3】:
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\""); 
echo readfile($url);

是正确的

对于 exe 类型的文件,或者更好的一个

header("Location: $url");

【讨论】:

  • @Fabio:将添加更多细节
  • 这非常简单。有效。那么 file-Get_contents 是什么?只是好奇。谢谢。
  • 下载到你的服务器
  • 只要去掉'readfile()'之前的'echo',因为返回值被指定为“返回从文件中读取的字节数。如果发生错误,则返回FALSE,除非函数被称为@readfile(),会打印一条错误消息。”。所以你最终会得到文件的内容+内容末尾的整数。
【解决方案4】:

试试这个:

header('Content-type: audio/mp3'); 
header('Content-disposition: attachment; 
filename=“'.$trackname'”');                             
readfile('folder name /'.$trackname);          
exit();

【讨论】:

    【解决方案5】:

    http://php.net/manual/en/function.readfile.php

    这就是你所需要的。 “Monkey.gif”改成你的文件名。如果需要从其他服务器下载,“monkey.gif”改为“http://www.exsample.com/go.exe”

    【讨论】:

    • 欢迎来到 StackOverflow。始终建议直接包含代码而不是添加链接以避免链接问题。
    【解决方案6】:

    对上面接受的答案的修改,它还在运行时检测 MIME 类型:

    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    header('Content-Type: '.finfo_file($finfo, $path));
    
    $finfo = finfo_open(FILEINFO_MIME_ENCODING);
    header('Content-Transfer-Encoding: '.finfo_file($finfo, $path)); 
    
    header('Content-disposition: attachment; filename="'.basename($path).'"'); 
    readfile($path); // do the double-download-dance (dirty but worky)
    

    【讨论】:

      【解决方案7】:

      首先显示您的文件并将其值设置为 url。

      index.php

      <a href="download.php?download='.$row['file'].'" title="Download File">
      

      下载.php

      <?php
      /*db connectors*/
      include('dbconfig.php');
      
      /*function to set your files*/
      function output_file($file, $name, $mime_type='')
      {
          if(!is_readable($file)) die('File not found or inaccessible!');
          $size = filesize($file);
          $name = rawurldecode($name);
          $known_mime_types=array(
              "htm" => "text/html",
              "exe" => "application/octet-stream",
              "zip" => "application/zip",
              "doc" => "application/msword",
              "jpg" => "image/jpg",
              "php" => "text/plain",
              "xls" => "application/vnd.ms-excel",
              "ppt" => "application/vnd.ms-powerpoint",
              "gif" => "image/gif",
              "pdf" => "application/pdf",
              "txt" => "text/plain",
              "html"=> "text/html",
              "png" => "image/png",
              "jpeg"=> "image/jpg"
          );
      
          if($mime_type==''){
              $file_extension = strtolower(substr(strrchr($file,"."),1));
              if(array_key_exists($file_extension, $known_mime_types)){
                  $mime_type=$known_mime_types[$file_extension];
              } else {
                  $mime_type="application/force-download";
              };
          };
          @ob_end_clean();
          if(ini_get('zlib.output_compression'))
          ini_set('zlib.output_compression', 'Off');
          header('Content-Type: ' . $mime_type);
          header('Content-Disposition: attachment; filename="'.$name.'"');
          header("Content-Transfer-Encoding: binary");
          header('Accept-Ranges: bytes');
      
          if(isset($_SERVER['HTTP_RANGE']))
          {
              list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
              list($range) = explode(",",$range,2);
              list($range, $range_end) = explode("-", $range);
              $range=intval($range);
              if(!$range_end) {
                  $range_end=$size-1;
              } else {
                  $range_end=intval($range_end);
              }
      
              $new_length = $range_end-$range+1;
              header("HTTP/1.1 206 Partial Content");
              header("Content-Length: $new_length");
              header("Content-Range: bytes $range-$range_end/$size");
          } else {
              $new_length=$size;
              header("Content-Length: ".$size);
          }
      
          $chunksize = 1*(1024*1024);
          $bytes_send = 0;
          if ($file = fopen($file, 'r'))
          {
              if(isset($_SERVER['HTTP_RANGE']))
              fseek($file, $range);
      
              while(!feof($file) &&
                  (!connection_aborted()) &&
                  ($bytes_send<$new_length)
              )
              {
                  $buffer = fread($file, $chunksize);
                  echo($buffer);
                  flush();
                  $bytes_send += strlen($buffer);
              }
              fclose($file);
          } else
              die('Error - can not open file.');
          die();
      }
      set_time_limit(0);
      
      /*set your folder*/
      $file_path='uploads/'."your file";
      
      /*output must be folder/yourfile*/
      
      output_file($file_path, ''."your file".'', $row['type']);
      
      /*back to index.php while downloading*/
      header('Location:index.php');
      ?>
      

      【讨论】:

      • 你从哪里得到这个?看起来很强大
      • @jundell-agbo 很有趣,对于 crt 文件?
      • 这也适用于非常大的文件。很好的解决方案。但是`$chunksize = 1*(1024*1024);` 很慢。我尝试了多个值,发现 `$chunksize = 8*(1024*1024);` 正在使用所有可用带宽。
      【解决方案8】:

      如果您必须下载大小超过允许的内存限制(memory_limit ini 设置)的文件,这会导致 PHP Fatal error: Allowed memory size of 5242880 bytes exhausted 错误,您可以这样做:

      // File to download.
      $file = '/path/to/file';
      
      // Maximum size of chunks (in bytes).
      $maxRead = 1 * 1024 * 1024; // 1MB
      
      // Give a nice name to your download.
      $fileName = 'download_file.txt';
      
      // Open a file in read mode.
      $fh = fopen($file, 'r');
      
      // These headers will force download on browser,
      // and set the custom file name for the download, respectively.
      header('Content-Type: application/octet-stream');
      header('Content-Disposition: attachment; filename="' . $fileName . '"');
      
      // Run this until we have read the whole file.
      // feof (eof means "end of file") returns `true` when the handler
      // has reached the end of file.
      while (!feof($fh)) {
          // Read and output the next chunk.
          echo fread($fh, $maxRead);
      
          // Flush the output buffer to free memory.
          ob_flush();
      }
      
      // Exit to make sure not to output anything else.
      exit;
      

      【讨论】:

      • 我刚试过这个,它杀死了我的服务器。在我的日志中写入了大量错误。
      • 了解日志的内容会很有用。
      • 是的,抱歉,日志文件太大了,我只好对其进行核对,所以我没有看到。
      • 我不得不在我访问权限非常有限的服务器上设置类似的东西。我的下载脚本一直重定向到主页,我不知道为什么。现在我知道问题是内存错误,这段代码解决了它。
      • 如果你只使用ob_flush() 可能会出现错误:ob_flush(): failed to flush buffer。没有要刷新的缓冲区。用if (ob_get_level() &gt; 0) {ob_flush();} 环绕它(参考stackoverflow.com/a/9182133/128761)
      【解决方案9】:

      以下代码是在php中实现下载服务的正确方法,如下tutorial中所述

      header('Content-Type: application/zip');
      header("Content-Disposition: attachment; filename=\"$file_name\"");
      set_time_limit(0);
      $file = @fopen($filePath, "rb");
      while(!feof($file)) {
          print(@fread($file, 1024*8));
          ob_flush();
          flush();
      }
      

      【讨论】:

      • 这段代码中有两个变量:file_name 和 filePath。不是很好的编码习惯。尤其是不用解释。链接到外部教程没有帮助。
      【解决方案10】:

      您也可以流式下载,这将大大减少资源消耗。 示例:

      $readableStream = fopen('test.zip', 'rb');
      $writableStream = fopen('php://output', 'wb');
      
      header('Content-Type: application/octet-stream');
      header('Content-Disposition: attachment; filename="test.zip"');
      stream_copy_to_stream($readableStream, $writableStream);
      ob_flush();
      flush();
      

      在上面的示例中,我正在下载一个 test.zip(实际上是我本地机器上的 android studio zip)。 php://output 是只写流(通常由 echo 或 print 使用)。 之后,您只需要设置所需的标头并调用 stream_copy_to_stream(source, destination)。 stream_copy_to_stream() 方法充当管道,从源流(读取流)获取输入并将其通过管道传输到目标流(写入流),它还避免了允许的内存耗尽的问题,所以你实际上可以下载比你的 PHP memory_limit 更大的文件。

      【讨论】:

      • 流式下载是什么意思?
      • @ParsaYazdani 表示下载的数据会被分块。请查看流式传输的概念以获取更多详细信息。注意:这不是唯一在 PHP 中流式(块)下载文件的方法。但它肯定是最简单的方法之一。
      【解决方案11】:

      我上面的答案有效。但是,我想贡献一个方法来说明如何使用 GET 来执行它

      在您的 html/php 页面上

      $File = 'some/dir/file.jpg';
      <a href="<?php echo '../sumdir/download.php?f='.$File; ?>" target="_blank">Download</a>
      

      和download.php 包含

      $file = $_GET['f']; 
      
      header("Expires: 0");
      header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
      header("Cache-Control: no-store, no-cache, must-revalidate");
      header("Cache-Control: post-check=0, pre-check=0", false);
      header("Pragma: no-cache");
      
      $ext = pathinfo($file, PATHINFO_EXTENSION);
      $basename = pathinfo($file, PATHINFO_BASENAME);
      
      header("Content-type: application/".$ext);
      header('Content-length: '.filesize($file));
      header("Content-Disposition: attachment; filename=\"$basename\"");
      ob_clean(); 
      flush();
      readfile($file);
      exit;
      

      这应该适用于任何文件类型。这没有使用 POST 进行测试,但它可以工作。

      【讨论】:

        【解决方案12】:

        您可以使用 download 属性来强制下载文件:

        &lt;a href="https://test.com/aaa.exe" download&gt;click here to download&lt;/a&gt;

        【讨论】:

        • 该问题要求提供 PHP 解决方案。这是一个 HTML 解决方案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-03
        • 2010-11-30
        • 2011-11-12
        • 2020-10-31
        • 1970-01-01
        • 2012-01-19
        相关资源
        最近更新 更多