Scarica il file sul server dall'URL


341

Bene, questo sembra abbastanza semplice, e lo è. Tutto quello che devi fare per scaricare un file sul tuo server è:

file_put_contents("Tmpfile.zip", file_get_contents("http://someurl/file.zip"));

C'è solo un problema. E se hai un file di grandi dimensioni, come 100mb. Quindi, si esaurirà la memoria e non sarà possibile scaricare il file.

Quello che voglio è un modo per scrivere il file sul disco mentre lo sto scaricando. In questo modo, posso scaricare file più grandi, senza incorrere in problemi di memoria.


4
È impostato nella configurazione del tuo server, PHP non può davvero aggirarlo per quanto ne so (tranne che per una modifica diretta .ini)
Ben

Risposte:


494

Da PHP 5.1.0, file_put_contents()supporta la scrittura pezzo per pezzo passando un handle di flusso come $dataparametro:

file_put_contents("Tmpfile.zip", fopen("http://someurl/file.zip", 'r'));

Dal manuale:

Se i dati [che è il secondo argomento] è una risorsa di flusso, il buffer rimanente di quel flusso verrà copiato nel file specificato. Questo è simile con l'utilizzo stream_copy_to_stream().

(Grazie Hakre .)


4
Non sarebbe la mia prima scelta. Se allow_fopen_url Offè impostato in php.ini (buona idea per la sicurezza), il tuo script sarebbe rotto.
PleaseStand,

4
@idealmachine Penso file_get_contents()che non funzionerebbe neanche se fosse così (vedi OP).
alex,

10
@geoff Ero specifico, ho menzionato la funzione che volevi. Quello che avresti potuto desiderare era qualcuno che scrivesse il codice per te, ma sono sicuro che hai imparato qualcosa da solo. Inoltre, se vogliamo commentare le interazioni SO reciproche, ti preghiamo di accettare alcune altre risposte :)
alex

@alex: si prega di vedere la modifica, sentitevi liberi di incorporare. fammi sapere quando posso rimuovere questo commento qui allora.
Hacre,

4
Il flag 'b' dovrebbe anche essere usato nella maggior parte dei casi con fopen; impedisce effetti negativi alle immagini e ad altri file di testo non semplici.
Wayne Weibel,

132
private function downloadFile($url, $path)
{
    $newfname = $path;
    $file = fopen ($url, 'rb');
    if ($file) {
        $newf = fopen ($newfname, 'wb');
        if ($newf) {
            while(!feof($file)) {
                fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
            }
        }
    }
    if ($file) {
        fclose($file);
    }
    if ($newf) {
        fclose($newf);
    }
}

1
grazie per il tuo frammento, ma saresti in grado di spiegare il tuo codice @xaav? Non sono esattamente brillante a php. A cosa serve 1024 * 8? Grazie ancora.
vvMINOvv,

@wMINOw La lunghezza della linea.
David Bélanger,

2
In particolare, significa leggere fino a 8 KB alla volta (1024 byte per KB * 8) poiché il parametro è in byte. Finché la riga è <= 8 KB, leggerà l'intera riga contemporaneamente.
Doktor J,

1
Perché questa non è la risposta migliore?
GunJack il

1
Come gestite gli errori con questo approccio? Cosa succede se viene restituito un 404 o la connessione viene interrotta o il timeout?
Adam Swinden,

67

Prova a usare cURL

set_time_limit(0); // unlimited max execution time
$options = array(
  CURLOPT_FILE    => '/path/to/download/the/file/to.zip',
  CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
  CURLOPT_URL     => 'http://remoteserver.com/path/to/big/file.zip',
);

$ch = curl_init();
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);

Non sono sicuro ma credo con l' CURLOPT_FILEopzione che scrive mentre estrae i dati, vale a dire. non tamponato.


2
Normalmente, questo andrebbe bene, ma ho questo codice in un'app Web, quindi non posso essere sicuro che gli utenti avranno installato cURL. Tuttavia, ho votato a favore.
xaav,

@Geoff è un'app Web distribuita? Perché se controlli l'hosting, non ha importanza per i tuoi utenti (cURL è una libreria sul tuo server).
alex,

No. Non controllo l'hosting. È un'app Web distribuita che chiunque potrebbe avere.
xaav,

3
Il ricciolo potrebbe mancare. Ma quasi tutte le società di hosting condiviso hanno CURL installato per impostazione predefinita. Voglio dire, non ne ho visto uno che non lo fa.
Mangirdas Skripka,

19
A partire dai miei test, non puoi assegnare direttamente a CURLOPT_FILE un percorso file. Deve essere un gestore di file. Innanzitutto, apri il file con $fh = fopen('/path/to/download/the/file/to.zip', 'w');e chiudi con fclose($fh);after curl_close($ch);. E setCURLOPT_FILE => $fh
Gustavo

22

la risposta di prodigitalson non ha funzionato per me. Ho missing fopen in CURLOPT_FILE maggiori dettagli .

Questo ha funzionato per me, inclusi gli URL locali:

function downloadUrlToFile($url, $outFileName)
{   
    if(is_file($url)) {
        copy($url, $outFileName); 
    } else {
        $options = array(
          CURLOPT_FILE    => fopen($outFileName, 'w'),
          CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
          CURLOPT_URL     => $url
        );

        $ch = curl_init();
        curl_setopt_array($ch, $options);
        curl_exec($ch);
        curl_close($ch);
    }
}

19
  1. Creare una cartella denominata "download" nel server di destinazione
  2. Salvare [questo codice] nel .phpfile ed eseguirlo nel server di destinazione

Downloader:

<html>
<form method="post">
<input name="url" size="50" />
<input name="submit" type="submit" />
</form>
<?php
    // maximum execution time in seconds
    set_time_limit (24 * 60 * 60);

    if (!isset($_POST['submit'])) die();

    // folder to save downloaded files to. must end with slash
    $destination_folder = 'downloads/';

    $url = $_POST['url'];
    $newfname = $destination_folder . basename($url);

    $file = fopen ($url, "rb");
    if ($file) {
      $newf = fopen ($newfname, "wb");

      if ($newf)
      while(!feof($file)) {
        fwrite($newf, fread($file, 1024 * 8 ), 1024 * 8 );
      }
    }

    if ($file) {
      fclose($file);
    }

    if ($newf) {
      fclose($newf);
    }
?>
</html> 

Ciò presuppone che l'utente desideri uno script autonomo piuttosto che una soluzione che funzioni all'interno di un'applicazione PHP esistente e credo che quest'ultimo sia ciò che l'OP e la maggior parte degli altri stanno cercando. Una spiegazione sarebbe utile anche per le persone che vogliono capire l'approccio.
Sean the Bean,

1
ogni volta che provo sempre la mia dimensione del file trasferito è 50816 ma la mia dimensione del file è maggiore di questa .. 120 MB .. Qualche idea sul perché?
Riffaz Starr

set_time_limit (24 * 60 * 60);deve essere inserito in un ciclo. Non ha alcun effetto all'inizio della sceneggiatura.
Viktor Joras,

16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);

la tua risposta è molto semplice e funziona bene, mi ha aiutato dove cURL non è riuscito a ottenere il file, ha funzionato. Grazie :)
Tommix,

2
Potresti voler spiegare cosa fa realmente.
alex,

6
Questo non risolve il problema del PO di superare il limite di memoria PHP.
user9645

Questo è piuttosto semplice e diretto. Abbastanza utile per i casi più semplici in cui i file sono piccoli o l'ambiente è uno sviluppo locale.
Valentine Shi,

qualche idea per i file .xlsx? Memorizza file vuoto con memoria a 0 byte.
Dhruv Thakkar,

9

Ci sono 3 modi:

  1. file_get_contents e file_put_contents
  2. ARRICCIARE
  3. fopen

Puoi trovare esempi da qui .


8

Usa un metodo semplice in php copy()

copy($source_url, $local_path_with_file_name);

Nota: se il file di destinazione esiste già, verrà sovrascritto

Copia PHP () Funzione

Nota: è necessario impostare l'autorizzazione 777 per la cartella di destinazione. Utilizzare questo metodo durante il download sul computer locale.

Nota speciale: 777 è un'autorizzazione nel sistema basato su Unix con l'autorizzazione completa di lettura / scrittura / esecuzione al proprietario, al gruppo e a tutti. In generale, diamo questa autorizzazione alle risorse che non sono molto necessarie per essere nascoste al pubblico su un server web. Esempio: cartella immagini.


1
Non imposterò mai 777 come permanenti su un server web e darò il via a qualsiasi sviluppatore web che ha la cattiva idea di farlo. Ogni volta, ovunque. Stai attento ! Non puoi farlo ! Pensa alla sicurezza. Seguire le regole OWASP non è sufficiente. Avere un buon pensiero sulle cose semplici è importante.
ThierryB,

@ThierryB. Nota: ho fornito il percorso locale. e questo può essere usato in applicazioni interne. Buona lettura e comprensione delle domande e delle risposte. Pensa a diversi scenari. E questa non è accettata / migliore risposta. Ogni domanda ha risposte diverse con pro e contro. Esempio da capire: anche Fibonacci ha molteplici soluzioni uniche in cui solo una sarà la migliore. Altri verranno utilizzati in diversi scenari.
Pradeep Kumar Prabaharan,

Ok, ma prendere tempo per pensare alle migliori pratiche e implementarle all'interno di luoghi sicuri ti darà una migliore comprensione dei concetti che devi implementare. Forse se un intruso è nella tua casa ($), fa delle trappole o costruisce cose nel modo migliore che puoi dargli un po 'di mal di testa;)
ThierryB,

5

Lo uso per scaricare il file

function cURLcheckBasicFunctions()
{
  if( !function_exists("curl_init") &&
      !function_exists("curl_setopt") &&
      !function_exists("curl_exec") &&
      !function_exists("curl_close") ) return false;
  else return true;
}

/*
 * Returns string status information.
 * Can be changed to int or bool return types.
 */
function cURLdownload($url, $file)
{
  if( !cURLcheckBasicFunctions() ) return "UNAVAILABLE: cURL Basic Functions";
  $ch = curl_init();
  if($ch)
  {

    $fp = fopen($file, "w");
    if($fp)
    {
      if( !curl_setopt($ch, CURLOPT_URL, $url) )
      {
        fclose($fp); // to match fopen()
        curl_close($ch); // to match curl_init()
        return "FAIL: curl_setopt(CURLOPT_URL)";
      }
      if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_HEADER, $curlopt_header)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects) ) return "FAIL: curl_setopt(CURLOPT_MAXREDIRS)";

        return curl_exec($ch);
    } else {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_HEADER, true)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_RETURNTRANSFER, true)) return "FAIL: curl_setopt(CURLOPT_RETURNTRANSFER)";
        if( !curl_setopt($ch, CURLOPT_FORBID_REUSE, false)) return "FAIL: curl_setopt(CURLOPT_FORBID_REUSE)";
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
    }
      // if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true) ) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
      // if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
      // if( !curl_setopt($ch, CURLOPT_HEADER, 0) ) return "FAIL: curl_setopt(CURLOPT_HEADER)";
      if( !curl_exec($ch) ) return "FAIL: curl_exec()";
      curl_close($ch);
      fclose($fp);
      return "SUCCESS: $file [$url]";
    }
    else return "FAIL: fopen()";
  }
  else return "FAIL: curl_init()";
}

4

Una soluzione PHP 4 e 5:

readfile () non presenterà alcun problema di memoria, anche durante l'invio di file di grandi dimensioni, da solo. Un URL può essere utilizzato come nome file con questa funzione se i wrapper fopen sono stati abilitati.

http://php.net/manual/en/function.readfile.php


1
Questo non risponde alla domanda, perché la domanda riguarda la scrittura sul disco e non nel buffer di output.
Lorenz Meyer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.