PHP 强制下载导致0字节文件

7
我是一名有用的助手,可以为您翻译文本。以下是需要翻译的内容:

我正在尝试使用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);

有人能帮忙吗? 谢谢。


2
你确定给定的文件路径存在并且具有一些内容吗? - Gumbo
1
只是一个小提示,但你应该注意目录遍历。潜在攻击者可以使用“../../../../var/www/config.php”等方式,并可能读取一些敏感数据。 - user499054
有没有解决这个问题的方案,我也遇到了同样的问题。 - Braian Mellor
8个回答

9

您没有检查文件是否存在。尝试使用以下代码:

$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头。


1
这个确实可以运行,我认为你需要补充一下你使用的浏览器、文件类型、文件位置、文件和目录的权限以及你正在运行的服务器。 - RobertPitt
谢谢。我只有PPT文件时遇到了零字节问题。在stream_get_contents()之前使用ob_clean()和flush()有所帮助。 - pdu
1
根据PHP网站http://www.php.net/manual/en/function.readfile.php#102137的说明,header('Content-Length: ' . filesize($file));
$file应该是文件的完整路径。否则,内容长度不会总是设置,常常导致可怕的“0字节文件”问题。或者可以尝试像这样设置header('Content-Length: ' . filesize($filePath)); 先像这样设置路径,它立即对我起作用并且0字节问题得到解决!!!
- webblover

4

我在 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');

@Alex:你的$filename变量保存了什么?是来自数据库的BLOB吗?如果$filename是一个有效的文件系统路径,并且你的数据库方法没有抛出任何错误,那么这应该可以工作...你使用的是什么浏览器? - Alix Axel
1
@Alex:我认为这可能是问题所在……尝试指定文件的绝对路径,例如$filename = 'C:/www/test.zip'; - Alix Axel
1
@Alix:如果我想要创建自己的CDN,那么我会设置一个带有延迟池的Squid缓存:http://www.faqs.org/docs/Linux-HOWTO/Bandwidth-Limiting-HOWTO.html - Daniel Trebbien
1
@Alex:你用的是Linux还是Windows?假设Letter.txt文件的绝对路径为/home/user/docs/Letter.txt,如果你当前的工作目录是/home/user/,那么该文件的相对路径就是./docs/Letter.txt,但无论你在哪里,该文件的绝对路径始终不变(/home/user/docs/Letter.txt)...这个问题很难在评论中清楚地解释,我需要了解你的文件系统结构才能给出具体答案,请阅读这篇文章(http://en.wikipedia.org/wiki/Path_(computing)),或许可以帮到你。 - Alix Axel
为了快速下载,请移除 sleep(1); - Alex78191
显示剩余13条评论

3
你需要指定 Content-Length 头部:
header("Content-Length: " . filesize($filename));

此外,您不应该发送 Content-Transfer-Encoding 标头。根据HTTP/1.0HTTP/1.1 规范,“HTTP 不使用 RFC 1521 的 Content-Transfer-Encoding (CTE) 字段”。


2
这个问题和我的网站项目一样。我使用了以下代码:
<?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设置或缓存设置,但我不知道该怎么做。

1

我正在做这个来下载一个PDF文件...

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

我认为你漏掉了最后一行,即实际发送$file中文件内容的行。

Pablo


0

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

$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);

0

如果脚本可以处理小文件但对于大文件返回0字节文件,则必须通过php.ini增加memory_limit。 此外,您可以在代码之前添加以下行:

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

0
关键是使用file_exists检查以确认正确的路径。 最大的困惑在于,对于PHP路径,您不需要以“/”开头来表示您的网站起始路径。
 '/folder/file.ext'  #bad, needs to know the real full path

在 PHP 中,/ 已经是网站的主路径了,你不需要再提及它。只需写:

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

这将与 file_exists、header 文件名、readfile 等一起使用...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接