Pobierz plik na serwer z adresu URL


341

Cóż, ten wydaje się dość prosty i jest. Aby pobrać plik na serwer, wystarczy:

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

Jest tylko jeden problem. Co jeśli masz duży plik, na przykład 100 MB. Wówczas zabraknie pamięci i nie będzie można pobrać pliku.

To, czego chcę, to sposób na zapisanie pliku na dysku podczas jego pobierania. W ten sposób mogę pobierać większe pliki bez problemów z pamięcią.


4
Jest to ustawione w konfiguracji serwera, PHP nie może tego obejść (o ile nie mam bezpośredniej edycji .ini)
Ben

Odpowiedzi:


494

Od PHP 5.1.0 file_put_contents()obsługuje pisanie kawałek po kawałku, przekazując uchwyt strumienia jako $dataparametr:

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

Z instrukcji:

Jeśli dane [to drugi argument] są zasobem strumienia, pozostały bufor tego strumienia zostanie skopiowany do określonego pliku. Podobnie jest z używaniem stream_copy_to_stream().

(Dzięki Hakre .)


4
To nie byłby mój pierwszy wybór. Jeśli allow_fopen_url Offjest ustawiony w php.ini (dobry pomysł na bezpieczeństwo), twój skrypt zostałby uszkodzony.
PleaseStand

4
@idealmachine Myślę, że file_get_contents()to też by nie działało, gdyby tak było (patrz OP).
alex

10
@geoff Byłem konkretny, wspomniałem o funkcji, którą chciałeś. Być może chciałeś, żeby ktoś napisał dla ciebie kod - ale jestem pewien, że sam się nauczyłeś. Ponadto, jeśli mamy zamiar komentować wzajemne interakcje SO - proszę przyjąć więcej odpowiedzi :)
alex

@alex: Proszę zobaczyć edycję, prosimy o włączenie. daj mi znać, kiedy będę mógł usunąć ten komentarz tutaj.
hakre

4
Flaga „b” powinna być również używana w większości przypadków z fopen; zapobiega niekorzystnym skutkom obrazów i innych nie zwykłych plików tekstowych.
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
dziękuję za twój snippit, ale czy byłbyś w stanie wyjaśnić swój kod @xaav? Nie jestem do końca genialny w php. Do czego służy 1024 * 8? Dzięki jeszcze raz.
vvMINOvv,

@wMINOw Długość linii.
David Bélanger

2
W szczególności oznacza to odczyt do 8 KB na raz (1024 bajty na KB * 8), ponieważ parametr jest w bajtach. Dopóki linia ma <= 8 KB, odczyta całą linię na raz.
Doktor J

1
Dlaczego nie jest to najlepsza odpowiedź?
GunJack,

1
Jak radzisz sobie z błędami przy takim podejściu? Co się stanie, jeśli 404 zostanie zwrócony lub połączenie zostanie przerwane lub upłynie limit czasu?
Adam Swinden

67

Spróbuj użyć 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);

Nie jestem pewien, ale wierzę w CURLOPT_FILEopcję, która zapisuje podczas pobierania danych, tj. nie buforowane.


2
Normalnie byłoby dobrze, ale mam ten kod w aplikacji internetowej, więc nie mogę się upewnić, że użytkownicy będą mieć zainstalowany CURL. Dałem jednak głos.
xaav,

@Geoff to rozproszona aplikacja internetowa? Ponieważ jeśli kontrolujesz hosting, nie ma to znaczenia dla twoich użytkowników (cURL to biblioteka na twoim serwerze).
alex

Nie. Nie kontroluję hostingu. Jest to rozproszona aplikacja internetowa, którą każdy może mieć.
xaav

3
Curl może brakować. Ale prawie wszystkie współdzielone firmy hostingowe mają domyślnie zainstalowany CURL. To znaczy, nie widziałem takiego, który tego nie robi.
Mangirdas Skripka

19
Z moich testów nie można bezpośrednio przypisać do CURLOPT_FILE ścieżki pliku. To musi być moduł obsługi plików. Najpierw otwórz plik za pomocą $fh = fopen('/path/to/download/the/file/to.zip', 'w');i zamknij za pomocą fclose($fh);po curl_close($ch);. I ustawCURLOPT_FILE => $fh
Gustavo

22

Odpowiedź prodigitalona nie działała dla mnie. Mam missing fopen in CURLOPT_FILE więcej szczegółów .

To zadziałało dla mnie, w tym lokalnych adresów URL:

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. Utwórz folder o nazwie „pliki do pobrania” na serwerze docelowym
  2. Zapisz [ten kod] do .phppliku i uruchom na serwerze docelowym

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> 

Zakłada się, że użytkownik chce autonomicznego skryptu, a nie rozwiązania, które będzie działać w istniejącej aplikacji PHP, i uważam, że ten drugi jest tym, czego OP i większość innych szuka. Wyjaśnienie byłoby również pomocne dla osób, które chcą zrozumieć to podejście.
Sean the Bean

1
za każdym razem, gdy próbuję, mój przesyłany rozmiar pliku to 50816, ale mój rozmiar jest większy niż ten ... 120 MB .. Wiesz, dlaczego to jest?
Riffaz Starr

set_time_limit (24 * 60 * 60);musi być umieszczony w pętli. Na początku skryptu nie działa.
Viktor Joras

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

twoja odpowiedź jest bardzo prosta i dobrze działa, pomogła mi tam, gdzie cURL nie mógł pobrać pliku, to zadziałało. Dzięki :)
Tommix

2
Możesz wyjaśnić, co to właściwie robi.
alex

6
To nie rozwiązuje problemu PO przekroczenia limitu pamięci PHP.
user9645

To jest dość proste i jednoznaczne. Przydatne w prostszych przypadkach, gdy pliki są małe lub środowisko jest opracowaniem lokalnym.
Valentine Shi

jakiś pomysł na pliki .xlsx? Przechowuje pusty plik z pamięcią 0 bajtów.
Dhruv Thakkar

9

Istnieją 3 sposoby:

  1. file_get_contents i file_put_contents
  2. KĘDZIOR
  3. fopen

Można znaleźć przykłady stąd .


8

Użyj prostej metody w php copy()

copy($source_url, $local_path_with_file_name);

Uwaga: jeśli plik docelowy już istnieje, zostanie zastąpiony

Funkcja PHP copy ()

Uwaga: Musisz ustawić uprawnienie 777 dla folderu docelowego. Użyj tej metody podczas pobierania na komputer lokalny.

Uwaga specjalna: 777 to uprawnienie w systemie uniksowym z pełnym uprawnieniem do odczytu / zapisu / wykonania dla właściciela, grupy i wszystkich. Zasadniczo dajemy to zezwolenie na zasoby, które nie są zbytnio potrzebne do ukrywania przed publicznością na serwerze WWW. Przykład: folder zdjęć.


1
Nigdy nigdy nie ustawię 777 jako perms na serwerze WWW i wykopię każdego programistę, który ma zły pomysł, aby to zrobić. Zawsze i wszędzie. Bądź ostrożny ! Nie możesz tego zrobić ! Pomyśl o bezpieczeństwie. Przestrzeganie zasad OWASP nie wystarczy. Ważne jest dobre myślenie o prostych rzeczach.
ThierryB

@ThierryB. Uwaga: podałem ścieżkę lokalną. i można to wykorzystać w aplikacjach wewnętrznych. Dobra znajomość i zrozumienie pytań i odpowiedzi. Pomyśl o różnych scenariuszach. I to nie jest akceptowane / najlepsza odpowiedź. Każde pytanie ma inne odpowiedzi, w tym plusy i minusy. Przykład, który musisz zrozumieć: nawet Fibonacci ma wiele unikalnych rozwiązań, w których tylko jedno będzie najlepsze. Inne zostaną wykorzystane w różnych scenariuszach.
Pradeep Kumar Prabaharan

Ok, ale poświęcenie czasu na przemyślenie najlepszych praktyk i wdrożenie ich w zabezpieczonych miejscach zapewni lepsze zrozumienie pojęć, które należy wdrożyć. Może jeśli intruz jest w twoim domu ($), robienie pułapek lub budowanie rzeczy najlepiej, jak możesz, sprawi mu ból głowy;)
ThierryB

5

Używam tego do pobierania pliku

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

Rozwiązanie PHP 4 i 5:

readfile () nie stwarza żadnych problemów z pamięcią, nawet przy wysyłaniu dużych plików. Za pomocą tej funkcji można użyć adresu URL jako nazwy pliku, jeśli włączone są opakowania fopen.

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


1
To nie odpowiada na pytanie, ponieważ pytanie dotyczy zapisu na dysku, a nie do bufora wyjściowego.
Lorenz Meyer
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.