PHP的readfile()函数导致文件下载损坏

16

我正在使用php脚本,在必要的javascript计时器后提供从我的网站下载,此php脚本导致下载。但是无论我尝试什么,下载的文件都是损坏的。有人可以帮我指出我错在哪里吗。

这是我的代码

     <?php
include "db.php";    
 $id = htmlspecialchars($_GET['id']);
 $error = false;
    $conn = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD);
    if(!($conn)) echo "Failed To Connect To The Database!";
    else{   
        if(mysql_select_db(DB_NAME,$conn)){
            $qry = "SELECT Link FROM downloads WHERE ID=$id";
            try{
                $result = mysql_query($qry);
                if(mysql_num_rows($result)==1){
                    while($rows = mysql_fetch_array($result)){
                        $f=$rows['Link'];
                    }
                    //pathinfo returns an array of information
                    $path = pathinfo($f);
                    //basename say the filename+extension
                    $n = $path['basename'];
                    //NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
                    header('Content-type: application/octet-stream');
                    header('Content-Length: ' . filesize($f));
                    //This would be the one to rename the file
                    header('Content-Disposition: attachment; filename='.$n.'');
                    //Finally it reads the file and prepare the output
                    readfile($f);
                    exit();
                }else $error = true;
            }catch(Exception $e){
                $error = true;
            }
            if($error) 
            {
                header("Status: 404 Not Found");
                }
        }
  }
?> 

4
请勿使用mysql_*函数,因为它们正在弃用过程中。改用MySQLiPDO,成为一个更好的PHP开发者(http://jason.pureconcepts.net/2012/08/better-php-developer/)。 - Jason McCreary
3
你在 <?php 的开头前面有5个空格吗?你开启了错误报告吗? - Lawrence Cherone
2
移除 PHP 标签前的任何空格,使用 ob_start 开始脚本,并在 readfile 之前执行 ob_clean - air4x
我正在学习PDO...感谢您的建议。顺便说一下,这个脚本正在000webhost上运行,免费的Web托管服务不太可能很快更改PHP版本... - Imvikky
3个回答

29

在打开更多输出缓冲区的情况下,这对我很有帮助。

//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//clean all levels of output buffering
while (ob_get_level()) {
    ob_end_clean();
}
readfile($f);
exit();

1
那个“while”循环是解决我的问题的方法:下载后缺少了几个字节。我不知道它们是如何相关的,但这解决了我的问题。 - Absulit

15

首先,正如一些人在评论中指出的那样,在第一行 PHP 起始标记 (<?php) 前删除所有空格,这样就可以解决问题了(除非此文件被某个其他文件包含或需要)。

当您在屏幕上打印任何内容时,即使是一个空格,您的服务器也会与要打印的内容一起发送标头(在此情况下是您的空格)。为防止发生这种情况,您可以:

a) 在完成编写标头之前不要打印任何东西;

b) 在脚本中的第一件事运行 ob_start(),编写内容,编辑标头,然后每当您希望将内容发送到用户的浏览器时,使用 ob_flush()ob_clean()

在 b) 中,即使您成功编写了标头而没有出错,空格也会破坏您的二进制文件。您应该只编写您的二进制内容,而不是带有二进制内容的几个空格。

ob_ 前缀代表输出缓冲区。调用 ob_start() 时,您告诉应用程序您输出的所有内容 (echoprintf 等) 应该保存在内存中,直到您显式地告诉它“去” (ob_flush()) 到客户端。这样,您就可以将输出与标头一起保存,当您完成编写它们时,它们将随着内容一起顺利地发送。


1
这是因为在php标签之前有空格...我一直很苦恼,但自己找不到...非常感谢您及时的帮助... - Imvikky
另外,如果你遇到了这个问题,请确保像文档中所述刷新输出缓冲区。 - Alex W
我已经寻找这样的答案好几天了...非常感谢,你节省了我很多时间! - Adrian Efford

4
           ob_start();//add this to the beginning of your code 

      if (file_exists($filepath) && is_readable($filepath) ) {
header('Content-Description: File Transfer');
 header("Content-Type: application/octet-stream");
 header("Content-Disposition: attachment; filename=$files");
 header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
   header('Pragma: public');
 header("content-length=".filesize($filepath));
 header("Content-Transfer-Encoding: binary");

   /*add while (ob_get_level()) {
       ob_end_clean();
         }  before readfile()*/
  while (ob_get_level()) {
ob_end_clean();
   }
    flush();
   readfile($filepath);

嘿,感谢你的回答 - 即使这是一个非常老的问题。 如果您能在代码片段周围添加更多信息以帮助任何发现此答案的人理解您的建议和原因,那将是很棒的。 - trs
这已经过去了几年,这解决了我的问题,确实很想知道背后的魔力是什么。 - tyrese humphreys

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