2013-04-11 5 views
7

Il mio file (126 MB, .exe) mi dà problemi.Perché i file di grandi dimensioni non vengono scaricati facilmente in Laravel?

Sto utilizzando il metodo di download standard di laravel.

Ho provato ad aumentare la memoria ma continua a dire che ho esaurito la memoria, oppure ho scaricato un file di dimensione da 0 KB.

La documentazione non parla di dimensioni di file di grandi dimensioni.

mio codice è

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

Tutto ciò che sto facendo male?

- Modifica -

Proseguendo Phill Sparks commento, questo è quello che ho e funziona. È una combinazione di Phill e alcuni di php.net. Non sono sicuro che manchi qualcosa?

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; 
} 

risposta

13

Questo accade perché Response::download() carichi il file in memoria prima di servire all'utente. Questo è un difetto nel framework, ma la maggior parte delle persone non tenta di servire file di grandi dimensioni attraverso il framework.

Soluzione 1 - Inserire i file che si desidera scaricare nella cartella pubblica, in un dominio statico o cdn - ignorare completamente Laravel.

Comprensibilmente, si potrebbe tentare di limitare l'accesso ai download da accesso, nel qual caso è necessario alle imbarcazioni il proprio metodo di download, qualcosa di simile a questo dovrebbe funzionare ...

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; 
} 

Questa funzione è una combinazione di Response::download() e di Laravel shutdown process. Non ho avuto la possibilità di testarlo da solo, non ho installato Laravel 3 al lavoro. Per favore fatemi sapere se fa il lavoro per voi.

PS: L'unica cosa di cui questo script non si occupa è i cookie. Sfortunatamente la funzione Response::cookies() è protetta. Se questo diventa un problema, puoi sollevare il codice dalla funzione e inserirlo nel metodo sendFile.

PPS: Ci potrebbe essere un problema con il buffer di output; se si tratta di un problema date un'occhiata nel manuale PHP allo readfile() examples, c'è un metodo che dovrebbe funzionare lì.

PPPS: Dal momento che si sta lavorando con i file binari che si potrebbe prendere in considerazione la sostituzione con readfile()fpassthru()

EDIT: PPS disconoscimento e il PPP, ho aggiornato il codice per utilizzare fread + stampare invece come questo sembra più stabile.

+0

Ha quasi funzionato, ho continuato a trovare file non trovati errori con sia readfile che fpassthru(). Ma ho guardato i link e l'ho combinato con il tuo per creare qualcosa che funzioni. Anche se non sono sicuro di quanto sia corretto? Ho modificato la mia domanda per mostrare l'ultima. – Lango

+0

Ciao @Lango, non vedo alcun problema con la tua soluzione. Ho aggiornato la mia risposta per includere l'approccio fread + print e anche un 'ob_get_clean()'. Non riesco a capire perché fpassthru non funzionerebbe quando fread + print lo fa, visto che usano lo stesso puntatore di file - ma se funziona per te allora tira con sé! –

+0

Grazie Phill! Funziona tutto ora. – Lango

0

è necessario aumentare il vostro memory_limit in questo modo:

è necessario aumentare memory_limit utilizzando la funzione ini_set es ini_set('memory_limit','128M');

Questo funziona per voi spero!

ho trovato la risposta qui: https://stackoverflow.com/a/12443644/1379394

+1

Non ini_set ("memory_limit", "- 1"); Fai quello? – Lango

+0

Deve essere la dimensione del file? – Lango

+0

no non deve essere la dimensione del file. e -1 non funziona. –

3

Sto utilizzando il metodo personalizzato readfile_chunked() come indicato in php.net here. Per laravel 3, ho esteso il metodo di risposta in questo modo:

Aggiungi questo file come 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; 

    } 
} 

Poi commentate questa linea in application/config/application.php:

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

Esempio:

//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; 

Opere grande finora.

2

È possibile utilizzare lo Symfony \ Component \ HttpFoundation \ StreamedResponse come questo:

$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; 

Per ulteriori informazioni, controllare this