2013-04-11 36 views
7

我的文件(126 MB大小,.exe)給我提出了問題。Laravel爲什麼不能輕鬆下載大文件?

我正在使用標準的laravel下載方法。

我試圖增加內存,但它仍然說我已經用完內存,或者我下載了一個0 KB大小的文件。

該文檔沒有提及任何有關大文件大小的內容。

我的代碼是

ini_set("memory_limit","-1"); // Trying to see if this works 
return Response::download($full_path); 

什麼我做錯了嗎?

- 編輯 -

你打算在菲爾火花評論,這是我和它的工作原理。 這是Phill's加上一些來自php.net的組合。 不確定是否有遺漏的東西?

public static function big_download($path, $name = null, array $headers = array()) 
{ 
    if (is_null($name)) $name = basename($path); 

    // Prepare the headers 
    $headers = array_merge(array(
     'Content-Description'  => 'File Transfer', 
     'Content-Type'    => File::mime(File::extension($path)), 
     'Content-Transfer-Encoding' => 'binary', 
     'Expires'     => 0, 
     'Cache-Control'    => 'must-revalidate, post-check=0, pre-check=0', 
     'Pragma'     => 'public', 
     'Content-Length'   => File::size($path), 
    ), $headers); 

    $response = new Response('', 200, $headers); 
    $response->header('Content-Disposition', $response->disposition($name)); 

    // If there's a session we should save it now 
    if (Config::get('session.driver') !== '') 
    { 
     Session::save(); 
    } 

    // Below is from http://uk1.php.net/manual/en/function.fpassthru.php comments 
    session_write_close(); 
    ob_end_clean(); 
    $response->send_headers(); 
    if ($file = fopen($path, 'rb')) { 
     while(!feof($file) and (connection_status()==0)) { 
      print(fread($file, 1024*8)); 
      flush(); 
     } 
     fclose($file); 
    } 

    // Finish off, like Laravel would 
    Event::fire('laravel.done', array($response)); 
    $response->foundation->finish(); 

    exit; 
} 

回答

13

這是因爲Response::download()加載到內存中的文件,將其投放到用戶之前。無可否認,這是該框架中的一個缺陷,但大多數人不會嘗試通過框架提供大文件。

解決方案1 ​​ - 將要下載的文件放入公用文件夾,靜態域或cdn中 - 完全繞過Laravel。

可以理解的,你可能會嘗試通過限制登錄訪問到您的下載,在這種情況下,你需要手藝自己的下載方式,這樣的事情應該工作...

function sendFile($path, $name = null, array $headers = array()) 
{ 
    if (is_null($name)) $name = basename($path); 

    // Prepare the headers 
    $headers = array_merge(array(
     'Content-Description'  => 'File Transfer', 
     'Content-Type'    => File::mime(File::extension($path)), 
     'Content-Transfer-Encoding' => 'binary', 
     'Expires'     => 0, 
     'Cache-Control'    => 'must-revalidate, post-check=0, pre-check=0', 
     'Pragma'     => 'public', 
     'Content-Length'   => File::size($path), 
    ), $headers); 

    $response = new Response('', 200, $headers); 
    $response->header('Content-Disposition', $response->disposition($name)); 

    // If there's a session we should save it now 
    if (Config::get('session.driver') !== '') 
    { 
     Session::save(); 
    } 

    // Send the headers and the file 
    ob_end_clean(); 
    $response->send_headers(); 

    if ($fp = fread($path, 'rb')) { 
     while(!feof($fp) and (connection_status()==0)) { 
      print(fread($fp, 8192)); 
      flush(); 
     } 
    } 

    // Finish off, like Laravel would 
    Event::fire('laravel.done', array($response)); 
    $response->foundation->finish(); 

    exit; 
} 

此功能是Response::download()和Laravel的shutdown process的組合。我沒有機會自己測試它,我沒有安裝Laravel 3。請讓我知道,如果它爲你做的工作。

PS:這個腳本不關心的唯一事情就是cookies。不幸的是,Response::cookies()功能受到保護。如果這成爲一個問題,您可以從函數中提取代碼並將其放入sendFile方法中。

PPS: There 可能是輸出緩衝的問題;如果這是一個問題,請查看PHP手冊readfile() examples,這裏有一個方法應該在那裏工作。

PPPS:由於您使用的二進制文件,則可能要考慮與fpassthru()

編輯替換readfile()無視PPS和購買力平價,我已經更新爲使用FREAD +代碼打印,因爲這看起來更穩定。

+0

它幾乎奏效,我一直使用readfile和fpassthru()獲取文件未找到erros。但是我查看了鏈接,並將其與您的鏈接合併在一起,以實現一些有效的功能。雖然我不確定它有多正確?我編輯了我的問題以顯示最新消息。 – Lango 2013-04-15 00:06:07

+0

嗨@Lango,我看不出您的解決方案有任何問題。我已經更新了我的答案,包括fread + print方法和'ob_get_clean()'。我不明白爲什麼fpassthru在fread +打印時無法工作,因爲它們使用相同的文件指針 - 但是如果它適用於您,然後隨它一起滾動! – 2013-04-15 08:51:26

+0

謝謝Phill!現在都在工作。 – Lango 2013-04-16 03:07:21

0

你需要增加你的memory_limit的,像這樣:

你需要增加memory_limit的使用中ini_set功能e.g ini_set('memory_limit','128M');

這會爲你,我希望努力!

我找到了答案在這裏:https://stackoverflow.com/a/12443644/1379394

+1

不的ini_set( 「memory_limit的」, 「 - 1」);去做? – Lango 2013-04-11 07:03:23

+0

它需要是文件的大小嗎? – Lango 2013-04-11 07:03:41

+0

no不一定是文件的大小。 -1不起作用。 – 2013-04-11 07:04:29

3

我正在使用readfile_chunked()自定義方法,如在php.net here中所述。對於Laravel 3,我已經延長了響應的方法是這樣的:

添加此文件作爲applications/libraries/response.php

<?php 
class Response extends Laravel\Response { 

    //http://www.php.net/manual/en/function.readfile.php#54295 
    public static function readfile_chunked($filename,$retbytes=true) { 
     $chunksize = 1*(1024*1024); // how many bytes per chunk 
     $buffer = ''; 
     $cnt =0; 
     // $handle = fopen($filename, 'rb'); 
     $handle = fopen($filename, 'rb'); 
     if ($handle === false) { 
      return false; 
     } 
     while (!feof($handle)) { 
      $buffer = fread($handle, $chunksize); 
      echo $buffer; 
      ob_flush(); 
      flush(); 
      if ($retbytes) { 
       $cnt += strlen($buffer); 
      } 
     } 
      $status = fclose($handle); 
     if ($retbytes && $status) { 
      return $cnt; // return num. bytes delivered like readfile() does. 
     } 
     return $status; 

    } 
} 

然後註釋掉的application/config/application.php這一行:

'Response'  => 'Laravel\\Response', 

示例代碼:

//return Response::download(Config::get('myconfig.files_folder').$file->upload, $file->title); 

header('Content-Description: File Transfer'); 
header('Content-Type: application/octet-stream'); 
header('Content-Disposition: attachment; filename='.$file->title); 
header('Content-Transfer-Encoding: binary'); 
header('Expires: 0'); 
header('Cache-Control: must-revalidate'); 
header('Pragma: public'); 
header('Content-Length: ' . File::size(Config::get('myconfig.files_folder').$file->upload)); 
ob_clean(); 
flush(); 
Response::readfile_chunked(Config::get('myconfig.files_folder').$file->upload); 
exit; 

迄今爲止工作良好。

2

可以使用的Symfony \分量\ HttpFoundation \ StreamedResponse這樣的:

$response = new StreamedResponse(
    function() use ($filePath, $fileName) { 
     // Open output stream 
     if ($file = fopen($filePath, 'rb')) { 
      while(!feof($file) and (connection_status()==0)) { 
       print(fread($file, 1024*8)); 
       flush(); 
      } 
      fclose($file); 
     } 
    }, 
    200, 
    [ 
     'Content-Type' => 'application/octet-stream', 
     'Content-Disposition' => 'attachment; filename="' . $fileName . '"', 
    ]); 

return $response; 

的更多信息,請this