【问题标题】:PHP Force Download Causing 0 Byte FilesPHP强制下载导致0字节文件
【发布时间】:2026-01-03 17:15:01
【问题描述】:

我正在尝试使用 PHP 从我的 Web 服务器强制下载文件。 我不是 PHP 专家,但我似乎无法解决以 0 字节大小下载文件的问题。

代码:

$filename = "FILENAME...";

header("Content-type: $type");
header("Content-Disposition: attachment;filename=$filename");
header("Content-Transfer-Encoding: binary");
header('Pragma: no-cache');
header('Expires: 0');
set_time_limit(0);
readfile($file);

有人可以帮忙吗? 谢谢。

【问题讨论】:

  • 您确定给定文件使用该路径存在并且有一些内容吗?
  • 只是一个小提示,但您应该注意目录遍历。例如,潜在的攻击者可以使用“../../../../var/www/config.php”并可能读取一些敏感数据。
  • 这个问题的任何解决方案,面临同样的问题

标签: php download


【解决方案1】:

您没有检查文件是否存在。试试这个:

$file = 'monkey.gif';

if (file_exists($file))
{
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
}else
{
    echo "File does not exists";
}

看看你会得到什么。


您还应该注意,这会强制下载为八位字节流,即纯二进制文件。一些浏览器将难以理解文件的确切类型。例如,如果您发送标头为Content-Type: application/octet-stream 的 GIF,则浏览器可能不会将其视为 GIF 图像。您应该添加特定检查以确定文件的内容类型,并发送适当的Content-Type 标头。

【讨论】:

  • 这确实有效,我认为您需要使用您使用的浏览器、文件是什么、它的位置、文件的权限以及 dir 以及您运行的服务器来扩展您的问题。
  • 感谢您。我只有 ppt 文件的 0 字节问题。 stream_get_contents() 之前的 ob_clean() 和 flush() 有帮助。
  • 感谢这为我节省了一些时间。
  • 根据 PHP 站点,php.net/manual/en/function.readfile.php#102137 header('Content-Length: ' .filesize($file)); $file 应该是文件的完整路径。否则内容长度不会总是被设置,通常会导致可怕的“0字节文件”问题。或者只是尝试这样的 header('Content-Length: ' .filesize($filePath));第一次设置这样的路径,它立即对我有用,0 BYTES 问题解决了!!!
【解决方案2】:

我在phunction 中使用了以下方法,到目前为止我还没有遇到任何问题:

function Download($path, $speed = null)
{
    if (is_file($path) === true)
    {
        $file = @fopen($path, 'rb');
        $speed = (isset($speed) === true) ? round($speed * 1024) : 524288;

        if (is_resource($file) === true)
        {
            set_time_limit(0);
            ignore_user_abort(false);

            while (ob_get_level() > 0)
            {
                ob_end_clean();
            }

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

            while (feof($file) !== true)
            {
                echo fread($file, $speed);

                while (ob_get_level() > 0)
                {
                    ob_end_flush();
                }

                flush();
                sleep(1);
            }

            fclose($file);
        }

        exit();
    }

    return false;
}

您可以简单地尝试一下:

Download('/path/to/file.ext');

【讨论】:

  • 不错的节流小实现 :)
  • -1 节流应该由服务器完成,而不是 PHP 脚本。我认为这是一种糟糕的风格。
  • @Alex:我认为这可能是问题所在...尝试指定文件的绝对路径,例如$filename = 'C:/www/test.zip';
  • @Alix:如果我想创建自己的 CDN,那么我会设置一个带有延迟池的 Squid 缓存:faqs.org/docs/Linux-HOWTO/Bandwidth-Limiting-HOWTO.html
  • @Alex:你使用的是 Linux 还是 Windows?假设Letter.txt 文件的绝对路径是/home/user/docs/Letter.txt,如果您当前的工作目录是/home/user/,那么文件的相对路径是./docs/Letter.txt 但是绝对路径总是相同的(/home/user/docs/Letter.txt)无论如何你在哪里......这有点难以在评论中清楚地解释,我必须知道你的文件系统结构才能给你一个具体的答案,阅读这个(en.wikipedia.org/wiki/Path_(computing))也许它可以帮助你。
【解决方案3】:

这个问题和我的网站项目一样。我用过的这段代码:

<?php

$file = $_SERVER["DOCUMENT_ROOT"].'/.../.../'.$_GET['file'];

 if(!file)
 {
     // File doesn't exist, output error
     die('file not found');
 }
 else
 {

    //$file_extension = strtolower(substr(strrchr($file,"."),1));
    $file_extension = end(explode(".", $file));

    switch( $fileExtension)
    {
    case "pdf": $ctype="application/pdf"; break;
    case "exe": $ctype="application/octet-stream"; break;
    case "zip": $ctype="application/zip"; break;
    case "doc": $ctype="application/msword"; break;
    case "xls": $ctype="application/vnd.ms-excel"; break;
    case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
    case "gif": $ctype="image/gif"; break;
    case "png": $ctype="image/png"; break;
    case "jpeg":
    case "jpg": $ctype="image/jpg"; break;
    default: $ctype="application/force-download";
    }

    nocache_headers();

     // Set headers
    header("Pragma: public"); // required
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: public"); // required for certain browsers
    header("Content-Description: File Transfer");
    header("Content-Type: $ctype");
    header("Content-Disposition: attachment; filename=".$file.";" );
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: ".filesize($file));

     readfile($file);
 }
 ?>

我认为问题出在服务器设置上,例如 PHP 设置或缓存设置,但我不知道该怎么做。

【讨论】:

    【解决方案4】:

    您需要指定Content-Length 标头:

    header("Content-Length: " . filesize($filename));
    

    此外,您不应发送 Content-Transfer-Encoding 标头。 HTTP/1.0HTTP/1.1 specs state 都表示“HTTP 不使用 RFC 1521 的 Content-Transfer-Encoding (CTE) 字段”。

    【讨论】:

      【解决方案5】:

      我这样做是为了下载 PDF ...

      $filename = 'Application.pdf';
      header("Content-Type: application/pdf");
      header("Content-Disposition: attachment; filename=$filename");
      echo $pdf;
      

      我认为您错过了最后一行,您实际上是在其中发送 $file 中所有内容的文件内容。

      巴勃罗

      【讨论】:

        【解决方案6】:

        当我将目录更改为文件位置时,文件打开正常。

        $reportLocation = REPORTSLOCATION;
        
        $curDir = getcwd();
        
        chdir($reportLocation);
        
        if (file_exists($filename)) {
        
            header('Content-Disposition: attachment; filename="' . $filename . '"');
            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
            header('Content-Length: ' . filesize($filename));
            header('Content-Transfer-Encoding: binary');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
        
            ob_clean();
            flush();
        
            readfile($filename);
        }
        chdir($curDir);
        

        【讨论】:

          【解决方案7】:

          诀窍是检查 file_exists 以确认正确的路径。 最大的困惑是,对于 PHP 路径,您不需要以“/”开头来表示您的网站开始路径。

           '/folder/file.ext'  #bad, needs to know the real full path
          

          php中的/已经是网站的主路径了,不用提了。只是:

           'folder/file.ext'   #relative to the / website
          

          这将适用于 file_exists、头文件名、readfile 等...

          【讨论】:

            【解决方案8】:

            如果脚本适用于小文件,但对于大文件返回 0 字节文件。您必须通过 php.ini 创建 memory_limit 您也可以在代码之前添加以下行

            ini_set('memory_limit','-1');
            

            【讨论】: