2013-04-11 92 views
7

私のファイル(126 MBのサイズ、.exe)は私に問題を与えています。Laravelで大きなファイルを簡単にダウンロードできないのはなぜですか?

私は標準のlaravelダウンロード方法を使用しています。

メモリを増やしてみましたが、メモリが不足しているとか、0KBのサイズのファイルをダウンロードしているというメッセージが表示されます。

ドキュメントには大きなファイルサイズについての記載はありません。

私のコードは私が間違っているの

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

何ですか?編集 - -

フィルスパークスコメントに行く

が、これは私が持っているものであるとそれがに動作します。 PhillとPhp.netの組み合わせです。 不足しているものがあるかどうか不明ですか?

あなたは、私が期待しini_set('memory_limit','128M');

これはあなたのために働くだろう例えばini_set関数を使用してのmemory_limitを増やす必要があります:あなたはそうのようなあなたのmemory_limitを増やす必要があり

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-bypass 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:このスクリプトでは処理しないのはCookieだけです。残念ながら、Response::cookies()関数は保護されています。これが問題になる場合は、関数からコードを持ち上げてsendFileメソッドに入れることができます。

PPS:は出力バッファリングに問題があります。それが問題であれば、PHPマニュアルのreadfile() examplesを見てください。そこではうまくいくはずのメソッドがあります。

PPPS:無視PPSとPPPS、私は関数fread +を使用するコードを更新しました:あなたはバイナリファイルで作業しているので、あなたはfpassthru()

EDITでreadfile()を置き換えることを検討することをお勧めしますこれはより安定しているように見える。

+0

ほとんどの場合、readfileとfpassthru()の両方でファイルが見つかりませんでした。しかし、私はリンクを見て、それをあなたのものと組み合わせて働くものを作りました。それがどれほど正しいのか分かりませんが?最新の情報を表示するために質問を編集しました。 – Lango

+0

こんにちは@Lango、私はあなたのソリューションには問題が表示されません。 fread + printのアプローチと 'ob_get_clean()'も含めるように答えを更新しました。 fread + printが同じファイルポインタを使うので、なぜfpassthruがうまくいかないのかわかりませんが、それがあなたのために働くのであれば、ロールしてください! –

+0

ありがとうPhill!それはすべて今働いている。 – Lango

0

私はここで答えを見つけました:https://stackoverflow.com/a/12443644/1379394

+1

( "のmemory_limit"、 " - 1")ini_setません。それを行う? – Lango

+0

ファイルのサイズが必要ですか? – Lango

+0

ファイルのサイズである必要はありません。 -1は機能しません。 –

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.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

関連する問題