2011-11-07 33 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;