2011-11-07 34 views
3

我想解决我从一个PHP脚本下载“zip”文件时遇到的问题。看起来,当我使用下面的代码下载文件时,下载的文件在文件的开头附加了一个额外的0A09,导致winzip抛出一个损坏错误。PHP读取文件()添加额外的字节到下载的文件

<?php 
$pagePermissions = 7; 
require_once ('util/check.php'); 
require_once ('util/file_manager.php'); 

$file_manager = new FileManager(); 

if ($_SERVER['REQUEST_METHOD'] == "GET") { 
if (isset($_GET['q']) && $_GET['q'] == 'logout') { 
    //require_once ('util/users.php'); 
    //$userdata = new Userdata(); 
    $userdata -> kill_session(); 
    header("Location: download.php"); 
    exit ; 
} 

if (isset($_GET['q']) && $_GET['q'] == 'fetch') { 
    if (isset($_GET['name'])) { 
     @apache_setenv('no-gzip', 1); 
     header("Content-length: " . filesize('upload/' . $_GET['name'])); 
     header('Content-type: application/zip'); 
     //header("Content-Disposition: attachment; filename=\"{$_GET['name']}\" "); 
     header("Content-Disposition: attachment; filename={$_GET['name']}"); 
     header('Content-Transfer-Encoding: binary'); 

     readfile('upload/' . $_GET['name']); 
     exit(); 
    } 
} 

} 
?> 

任何帮助将不胜感激,文件下载细通过直接链接,附加2个字节的文件的开头只发生彻底的验证码。 在此先感谢

+2

我希望你没有在生产环境中使用它,它允许访问者获取PHP有权访问的服务器中的_any_文件。 – igorw

+1

与您的错误无关,但是...筛选您的输入!否则,我可以在URL上提供?name = ../../../etc/passwd。 –

+0

@igorw:打我吧...... :) –

回答

20

删除最后的?>并检查您的开始标记位于脚本的第一个字符的第一行。 PHP文件不要必须以结束标记结束。您下载的文件包含一个(或多个)\r\n的原因是因为PHP将直接回显(输出)<?php ?>以外的任何内容。通常情况下,如果脚本不回显HTML,那么您将省略结束的PHP标记,因为它不是强制性的,IMO会产生比其他更多的麻烦。

** 编辑 **

如果你读了readfile PHP手册,你有一个有用的例子,几乎你在你的问题有代码,代码少两行:

@apache_setenv('no-gzip', 1); 
header("Content-length: " . filesize('upload/' . $_GET['name'])); 
header('Content-type: application/zip'); 
//header("Content-Disposition: attachment; filename=\"{$_GET['name']}\" "); 
header("Content-Disposition: attachment; filename={$_GET['name']}"); 
header('Content-Transfer-Encoding: binary'); 

// add these two lines 
ob_clean(); // discard any data in the output buffer (if possible) 
flush();  // flush headers (if possible) 

readfile('upload/' . $_GET['name']); 
exit(); 

如果您在此之后仍然存在问题,那么问题可能不在于您的PHP代码。

+2

还要确保'<?php'真的在脚本的第一行的开头,没有任何东西在之前。哦,并确保这些东西同样适用于您包含的任何其他PHP文件。 –

+0

这听起来像是一个空白的问题,但由于readfile在关闭之前? >会在readfile真的成为问题后出现空白?对于发送二进制文件,如果您有权访问所需的apache模块,我更愿意使用X-Sendfile。使用x-sendfile还会增加一些安全性,例如缺少GET净化apache web根目录之外的调用文件。 –

+0

是的。函数'readfile'输出文件然后返回。然后,如果脚本结尾处的结束标记后面有多余字符,则在关闭HTTP连接之前,所有内容都将被回显。 –

0

我遇到了与readfile()相关的类似问题。事实证明,我的php.ini文件启用了输出压缩功能,并且搞乱了试图检索文件的闪存模块。 (我猜它无法处理它。)

我所要做的就是在php.ini文件中禁用压缩:

zlib.output_compression = off 

或者,在你的脚本:

<?php ini_set('zlib.output_compression', 'Off'); ?> 

只是想在遇到其他人无法从readfile()输出中接收文件时共享此文件。

0

我在Joomla(2.5)有一个类似的问题,使用readfile将Excel(.xls)文件传回给用户。

我还注意到文本和xml文件在开始时也插入了一些代码,但几乎忽略它,因为xml &文本阅读器倾向于仍然打开文件。

我决定尝试Yanick的建议(而不是服务器压缩选项播放),ReadFile的前只需刷新缓冲区:

ob_clean(); // discard any data in the output buffer (if possible) 
flush();  // flush headers (if possible) 

变戏法似的,它的工作。我建议这是一个替代答案:突出显示根本原因,显示它可以修复Joomla问题,并且因为我有二进制和文本返回的混合。

只是添加(道歉,如果这是显而易见的!) - MIME类型设置工作得很好:

$document = JFactory::getDocument(); 
$document->setMimeEncoding($mimetype); 

我甚至没有需要设置“内容传输编码:二进制”时,MIME类型是应用程序/八位字节流。

1

对不起已故的答复.....

我不知道我是正确的,直到你投这个.....

编辑您的代码:

ob_start(""); 
//instead of ob_start(); with out a null callback 

ob_end_clean(); //at the end , Note : "important" add instead of ob_end_flush() 

ie;

ob_start(""); 
//header   
ob_end_clean(); 
0

我有同样的问题所以我用这个头,我得到了我的解决方案。

$filename = ABSPATH.'/wp-content/summary/user_content/'.trim($file); 
    header('Pragma: public'); 
    header('Expires: 0'); 
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 
    header('Content-Description: File Transfer'); 
    header('Content-Type: text/text'); 
    header('Content-Disposition: attachment; filename="'.$file.'"'); 
    header('Content-Transfer-Encoding: binary'); 
    header('Cache-Control: max-age=0'); 
    readfile($filename); 
    exit;