2011-06-01 40 views
8

我想向用戶提供大型zip文件。當有兩個併發連接時,服務器耗盡內存(RAM)。我將內存量從300MB增加到4GB(Dreamhost VPS),然後工作正常。如何在不使用太多內存的情況下強制下載大文件?

我需要讓很多超過2個併發連接更多。實際的4GB將允許20個併發連接(太糟糕)。

好,我使用的是當前的代碼,需要的內存雙則實際文件的大小。這太糟糕了。我想要將文件「流式傳輸」給用戶。所以我會分配不超過被傳送給用戶的塊。

下面的代碼是我使用CI中的一個(PHP框架):

ini_set('memory_limit', '300M'); // it was the maximum amount of memory from my server 
set_time_limit(0); // to avoid the connection being terminated by the server when serving bad connection downloads 
force_download("download.zip", file_get_contents("../downloads/big_file_80M.zip"));exit; 

的force_download功能如下(笨默認輔助函數):

function force_download($filename = '', $data = '') 
{ 
    if ($filename == '' OR $data == '') 
    { 
     return FALSE; 
    } 

    // Try to determine if the filename includes a file extension. 
    // We need it in order to set the MIME type 
    if (FALSE === strpos($filename, '.')) 
    { 
     return FALSE; 
    } 

    // Grab the file extension 
    $x = explode('.', $filename); 
    $extension = end($x); 

    // Load the mime types 
    @include(APPPATH.'config/mimes'.EXT); 

    // Set a default mime if we can't find it 
    if (! isset($mimes[$extension])) 
    { 
     $mime = 'application/octet-stream'; 
    } 
    else 
    { 
     $mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension]; 
    } 

    // Generate the server headers 
    if (strpos($_SERVER['HTTP_USER_AGENT'], "MSIE") !== FALSE) 
    { 
     header('Content-Type: "'.$mime.'"'); 
     header('Content-Disposition: attachment; filename="'.$filename.'"'); 
     header('Expires: 0'); 
     header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 
     header("Content-Transfer-Encoding: binary"); 
     header('Pragma: public'); 
     header("Content-Length: ".strlen($data)); 
    } 
    else 
    { 
     header('Content-Type: "'.$mime.'"'); 
     header('Content-Disposition: attachment; filename="'.$filename.'"'); 
     header("Content-Transfer-Encoding: binary"); 
     header('Expires: 0'); 
     header('Pragma: no-cache'); 
     header("Content-Length: ".strlen($data)); 
    } 

    exit($data); 
} 

我試着我在Google中發現了一些基於塊的代碼,但文件總是被損壞。可能是因爲糟糕的代碼。

任何人都可以幫助我嗎?

+1

您是否嘗試過重定向到用頭'Location'文件? – Dani 2011-06-01 02:27:07

+0

像你剛剛關閉給用戶直接鏈接到文件... – NotMe 2011-06-01 02:35:20

+0

我忘了告訴該文件夾中通過網絡不能訪問更好的聲音給我。這是出於安全原因。如果用戶通過身份驗證過程,我只是提供該文件。我會嘗試下面的建議,並會回來投票選出最佳答案。 – 2011-06-01 17:54:02

回答

3

this thread有一些想法。我不知道readfile()方法是否會節省內存,但聽起來很有希望。

+0

是,'readfile'確實節省內存,因爲它讀取被直接輸出到瀏覽器不存儲文件的每個塊它在一個變量中(所以不需要使用額外的內存,只需要文件塊)。 – 2011-06-01 13:13:34

+1

工作得很好。我只是添加了一些額外的標題,所以iPhone在下載時不會提示錯誤。 'header('Content-Disposition:attachment; filename =「download.zip」'); header('Expires:0'); header('Cache-Control:must-revalidate,post-check = 0,pre-check = 0'); header(「Content-Transfer-Encoding:binary」); header('Pragma:public'); header(「Content-Length:」.filesize($ filename)); readfile($ filename); exit;' – 2011-06-01 19:23:01

+0

很高興它的工作! – 2011-06-01 19:37:01

0

您不能在其中使用$ data和整個文件數據。嘗試傳遞給這個函數不是文件的內容而是它的路徑。接下來一次發送所有頭文件,之後使用fread()讀取此文件的一部分,並回顯該塊,調用flush()並重復。如果任何其他頭文件將在此期間發送,則最終傳輸將被損壞。

+0

你提出的是什麼'readfile'不 – 2011-06-01 13:14:12

+0

'readfile'讀取整個文件一次,在我的建議我用'fread'(可以使用'fgets'爲好),因爲如果該文件將在i'e 1MB閱讀塊,那麼當下一個塊被分配給同一個變量時,內存可以被釋放。 – 2011-06-01 15:58:51

+1

對不起,'readfile'以8K塊讀取文件。我實際上是通過php代碼挖掘出來的,因爲這吸引了我,並且證實了它。 [readfile source,line 1383](http://svn.php.net/viewvc/php/php-src/trunk/ext/standard/file.c?revision=311543&view=markup),'php_stream_passthru'裏面有DEFINED到'_php_stream_passthru' [(source,line 453)](http://svn.php.net/viewvc/php/php-src/trunk/main/php_streams.h?revision=311422&view=markup),後面的功能是在[這個文件中,線1314](http://svn.php.net/viewvc/php/php-src/trunk/main/streams/streams.c?revision=311545&view=markup) – 2011-06-02 09:45:39

0

符號連接的大文件到您的文檔根目錄(假設它不是一個唯一的授權文件),然後讓Apache的處理。 (你能接受字節這樣範圍以及)

+0

假設Apache啓用了FollowSymLinks,並且OP沒有使用PHP下載腳本來添加安全層,但是否則是一個好主意。 – 2011-06-01 03:59:09

3

你通過PHP發送該文件的內容($數據)?

如果是這樣,每個Apache進程處理這最終會成長到這個文件的大小,因爲這些數據將被緩存。

你唯一的解決方案是不通過PHP發送文件內容/數據並簡單地將用戶重定向到文件系統中的下載URL。

使用生成的和獨特的符號鏈接,或隱蔽的位置。

0

添加您ini_setSESSION_START();

相關問題