Laden Sie die Datei von der URL auf den Server herunter

341

Nun, dieser scheint ziemlich einfach zu sein, und das ist es auch. Alles, was Sie tun müssen, um eine Datei auf Ihren Server herunterzuladen, ist:

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

Es gibt nur ein Problem. Was ist, wenn Sie eine große Datei wie 100 MB haben? Dann geht Ihnen der Speicher aus und Sie können die Datei nicht herunterladen.

Was ich möchte, ist eine Möglichkeit, die Datei beim Herunterladen auf die Festplatte zu schreiben. Auf diese Weise kann ich größere Dateien herunterladen, ohne auf Speicherprobleme zu stoßen.

xaav
quelle
4
Das ist in Ihrer Serverkonfiguration festgelegt, PHP kann es meines Wissens nicht wirklich umgehen (außer bei einer direkten INI-Bearbeitung)
Ben

Antworten:

494

file_put_contents()Unterstützt seit PHP 5.1.0 das Schreiben Stück für Stück, indem ein Stream-Handle als $dataParameter übergeben wird:

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

Aus dem Handbuch:

Wenn Daten [das ist das zweite Argument] eine Stream-Ressource sind, wird der verbleibende Puffer dieses Streams in die angegebene Datei kopiert. Dies ist ähnlich wie bei der Verwendung stream_copy_to_stream().

(Danke Hakre .)

Alex
quelle
4
Das wäre nicht meine erste Wahl. Wenn allow_fopen_url Offin php.ini festgelegt (gute Idee für die Sicherheit), würde Ihr Skript beschädigt.
PleaseStand
4
@idealmachine Ich denke, file_get_contents()würde auch nicht funktionieren, wenn das der Fall wäre (siehe OP).
Alex
10
@geoff Ich war spezifisch, ich erwähnte die Funktion, die Sie wollten. Was Sie vielleicht wollten, war jemand, der den Code für Sie schreibt - aber ich bin sicher, Sie haben etwas gelernt, indem Sie es selbst gemacht haben. Auch wenn wir die SO-Interaktionen des anderen kommentieren wollen - bitte akzeptieren Sie weitere Antworten :)
Alex
@alex: Bitte beachten Sie die Bearbeitung, zögern Sie nicht zu integrieren. Lassen Sie mich wissen, wann ich diesen Kommentar hier dann entfernen kann.
hakre
4
Das 'b'-Flag sollte in den meisten Fällen auch mit verwendet werden fopen. Verhindert nachteilige Auswirkungen auf Bilder und andere nicht reine Textdateien.
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);
    }
}
xaav
quelle
1
Vielen Dank für Ihren Snippit, aber könnten Sie Ihren Code @xaav erklären? Ich bin nicht gerade brillant bei PHP. Wofür ist 1024 * 8? Danke noch einmal.
vvMINOvv
@wMINOw Die Länge der Zeile.
David Bélanger
2
Insbesondere bedeutet dies, bis zu 8 KB gleichzeitig zu lesen (1024 Byte pro KB * 8), da der Parameter in Byte angegeben ist. Solange die Zeile <= 8 KB groß ist, wird die gesamte Zeile auf einmal gelesen.
Doktor J
1
Warum ist das nicht die beste Antwort?
GunJack
1
Wie gehen Sie mit diesem Ansatz mit Fehlern um? Was ist, wenn ein 404 zurückgegeben wird oder die Verbindung unterbrochen wird oder eine Zeitüberschreitung auftritt?
Adam Swinden
67

Versuchen Sie es mit 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);

Ich bin nicht sicher, aber ich glaube mit der CURLOPT_FILEOption, die es schreibt, wenn es die Daten zieht, dh. nicht gepuffert.

prodigitalson
quelle
2
Normalerweise wäre dies in Ordnung, aber ich habe diesen Code in einer Web-App, sodass ich nicht sicher sein kann, ob Benutzer cURL installiert haben. Ich habe dies jedoch aufgegeben.
xaav
@Geoff ist es eine verteilte Web-App? Denn wenn Sie das Hosting steuern, spielt es keine Rolle für Ihre Benutzer (cURL ist eine Bibliothek auf Ihrem Server).
alex
Nein, ich kontrolliere das Hosting nicht. Es ist eine verteilte Web-App, die jeder haben kann.
xaav
3
Locke könnte fehlen. Aber fast alle Shared Hosting-Unternehmen haben standardmäßig CURL installiert. Ich meine, ich habe keinen gesehen, der das nicht tut.
Mangirdas Skripka
19
Nach meinen Tests können Sie CURLOPT_FILE keinen Dateipfad direkt zuweisen. Es muss ein File-Handler sein. Öffnen Sie zuerst die Datei mit $fh = fopen('/path/to/download/the/file/to.zip', 'w');und schließen Sie mit fclose($fh);nach curl_close($ch);. Und setzenCURLOPT_FILE => $fh
Gustavo
22

Die Antwort von prodigitalson hat bei mir nicht funktioniert. Ich habe missing fopen in CURLOPT_FILE mehr Details .

Dies funktionierte für mich, einschließlich lokaler URLs:

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);
    }
}
Kamil Kiełczewski
quelle
19
  1. Erstellen Sie einen Ordner mit dem Namen "Downloads" auf dem Zielserver
  2. Speichern Sie [diesen Code] in einer .phpDatei und führen Sie ihn auf dem Zielserver aus

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> 
stra8edge
quelle
Dies setzt voraus, dass der Benutzer ein eigenständiges Skript anstelle einer Lösung wünscht, die in einer vorhandenen PHP-Anwendung funktioniert, und ich glaube, dass letztere das ist, wonach das OP und die meisten anderen suchen. Eine Erklärung wäre auch hilfreich für Menschen, die den Ansatz verstehen wollen.
Sean the Bean
1
Wenn ich dies versuche, ist meine übertragene Dateigröße immer 50816, aber meine Dateigröße ist größer als diese. 120 MB. Irgendeine Idee, warum das so ist?
Riffaz Starr
set_time_limit (24 * 60 * 60);muss in eine Schleife gelegt werden. Es hat zu Beginn des Skripts keine Auswirkung.
Viktor Joras
16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);
Dimmy
quelle
Ihre Antwort ist sehr einfach und funktioniert gut. Hat mir geholfen, wo cURL keine Datei erhalten konnte. Das hat funktioniert. Danke :)
Tommix
2
Vielleicht möchten Sie erklären, was dies tatsächlich tut.
Alex
6
Dies behebt nicht das Problem des OP, das PHP-Speicherlimit zu überschreiten.
user9645
Das ist ziemlich einfach und unkompliziert. Sehr nützlich für einfachere Fälle, in denen die Dateien klein sind oder die Umgebung eine lokale Entwicklung ist.
Valentine Shi
Irgendeine Idee für XLSX-Dateien? Es speichert eine leere Datei mit 0 Byte Speicher.
Dhruv Thakkar
9

Es gibt drei Möglichkeiten:

  1. file_get_contents und file_put_contents
  2. CURL
  3. öffnen

Beispiele finden Sie hier .

Hoan Huynh
quelle
8

Verwenden Sie eine einfache Methode in PHP copy()

copy($source_url, $local_path_with_file_name);

Hinweis: Wenn die Zieldatei bereits vorhanden ist, wird sie überschrieben

PHP copy () Funktion

Hinweis: Sie müssen die Berechtigung 777 für den Zielordner festlegen. Verwenden Sie diese Methode, wenn Sie auf Ihren lokalen Computer herunterladen.

Besonderer Hinweis: 777 ist eine Berechtigung in einem Unix-basierten System mit vollständiger Lese- / Schreib- / Ausführungsberechtigung für Eigentümer, Gruppe und alle. Im Allgemeinen erteilen wir diese Berechtigung Assets, die nicht unbedingt benötigt werden, um auf einem Webserver vor der Öffentlichkeit verborgen zu werden. Beispiel: Bilderordner.

Pradeep Kumar Prabaharan
quelle
1
Ich werde niemals niemals 777 als Dauerwellen auf einem Webserver einstellen, und ich werde jeden Webentwickler starten, der die schlechte Idee dazu hat. Jederzeit überall. Sei vorsichtig ! Das kannst du nicht tun ! Denken Sie an Sicherheit. Das Befolgen der OWASP-Regeln reicht nicht aus. Gutes Denken über einfache Dinge ist wichtig.
ThierryB
@ ThierryB. Hinweis: Ich habe einen lokalen Pfad angegeben. & Dies kann in internen Anwendungen verwendet werden. Gutes Lesen und Verstehen von Fragen und Antworten. Denken Sie an verschiedene Szenarien. Und dies wird nicht akzeptiert / beste Antwort. Jede Frage hat unterschiedliche Antworten mit Vor- und Nachteilen. Beispiel für Ihr Verständnis: Selbst Fibonacci hat mehrere einzigartige Lösungen, bei denen nur eine die beste ist. Andere werden in verschiedenen Szenarien verwendet.
Pradeep Kumar Prabaharan
Ok, aber wenn Sie sich Zeit nehmen, um über Best Practices nachzudenken und diese an gesicherten Orten umzusetzen, erhalten Sie ein besseres Verständnis für die Konzepte, die Sie implementieren müssen. Vielleicht, wenn ein Eindringling in Ihrem ($) Haus ist, einige Fallen macht oder Dinge so gut wie möglich baut, wird er Kopfschmerzen bekommen;)
ThierryB
5

Ich benutze dies, um die Datei herunterzuladen

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()";
}
Hoàng Vũ Tgtt
quelle
4

Eine PHP 4 & 5 Lösung:

readfile () zeigt selbst beim Senden großer Dateien keine Speicherprobleme an. Mit dieser Funktion kann eine URL als Dateiname verwendet werden, wenn die fopen-Wrapper aktiviert wurden.

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

Eric Leroy
quelle
1
Dies beantwortet die Frage nicht, da es sich bei der Frage um das Schreiben auf die Festplatte und nicht in den Ausgabepuffer handelt.
Lorenz Meyer