Завантажте файл на сервер з URL-адреси


341

Ну, це здається досить простим, і воно є. Все, що вам потрібно зробити, щоб завантажити файл на свій сервер, це:

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

Тільки є одна проблема. Що робити, якщо у вас великий файл, наприклад 100mb. Тоді у вас залишиться пам'ять, і ви не зможете завантажити файл.

Що я хочу - це спосіб записати файл на диск під час його завантаження. Таким чином я можу завантажувати більші файли, не стикаючись з проблемами пам'яті.


4
Це встановлено в налаштуваннях вашого сервера, PHP насправді не може обійти його, наскільки я знаю (за винятком прямого редагування .ini)
Ben

Відповіді:


494

Оскільки PHP 5.1.0, file_put_contents()підтримує запис по частинах, передаючи ручку потоку як $dataпараметр:

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

З посібника:

Якщо дані [це другий аргумент] є ресурсом потоку, решта буфера цього потоку буде скопійовано у вказаний файл. Це схоже з використанням stream_copy_to_stream().

(Дякую Хакре .)


4
Це був би не мій перший вибір. Якщо allow_fopen_url Offвстановлено в php.ini (хороша ідея для безпеки), ваш сценарій буде порушений.
Будь ласка, продовжте

4
@idealmachine Я думаю, що file_get_contents()це не спрацювало, якби це було так (див. ОП).
алекс

10
@geoff Я був конкретним, я згадав про функцію, яку ви хотіли. Можливо, ви хотіли, щоб хтось написав вам код, але я впевнений, що ви навчились щось робити самостійно. Крім того, якщо ми будемо коментувати взаємодію SO один з одним - прийміть ще кілька відповідей :)
alex

@ Алекс: Перегляньте редагування, не соромтеся включатись. дайте мені знати, коли я можу потім видалити цей коментар тут.
хакре

4
Прапор 'b' також повинен використовуватися в більшості випадків з fopen; запобігає несприятливому впливу на зображення та інші непрості текстові файли.
Уейн Вейбель

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
дякую за ваш snippit, але чи зможете ви пояснити свій код @xaav? Я не зовсім блискучий на php. Для чого 1024 * 8? Знову дякую.
vvMINOvv

@wMINOw Довжина рядка.
Девід Белангер

2
Зокрема, це означає прочитати до 8 КБ одночасно (1024 байти за КБ * 8), оскільки параметр знаходиться в байтах. Поки рядок <= 8 КБ, він буде читати одразу весь рядок.
Doktor J

1
Чому це не найкраща відповідь?
GunJack

1
Як ви вирішуєте помилки при такому підході? Що робити, якщо 404 буде повернуто або з'єднання перервано або припинено?
Адам Свінден

67

Спробуйте використовувати 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);

Я не впевнений, але я вважаю, що CURLOPT_FILEваріант, який він пише, як витягує дані, тобто. не буферний


2
Як правило, це було б добре, але я маю цей код у веб-додатку, тому не можу бути впевненим, що у користувачів буде встановлено CURL. Однак я все-таки дав цей голос.
xaav

@Geoff це розповсюджений веб-додаток? Тому що якщо ви керуєте хостингом, це не має значення для ваших користувачів (cURL - це бібліотека на вашому сервері).
алекс

Ні. Я не контролюю хостинг. Це розповсюджений веб-додаток, який може мати кожен.
xaav

3
Можливо, Curl відсутній. Але майже всі спільні хостинг-компанії мають за замовчуванням встановлений CURL. Я маю на увазі, я не бачив того, хто цього не робить.
Mangirdas Skripka

19
З моїх тестів, ви не можете призначити CURLOPT_FILE шлях до файлу безпосередньо. Це має бути обробник файлів. Спочатку відкрийте файл за допомогою $fh = fopen('/path/to/download/the/file/to.zip', 'w');та закрийте fclose($fh);після curl_close($ch);. І встановитиCURLOPT_FILE => $fh
Густаво

22

prodigitalson в відповідь не працює для мене. Я отримав missing fopen in CURLOPT_FILE більше деталей .

Це працювало для мене, включаючи місцеві 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. Створіть папку під назвою "завантаження" на сервері призначення
  2. Збережіть [цей код] у .phpфайл та запустіть на сервері призначення

Завантажувач:

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

Це передбачає, що користувач хоче автономний сценарій, а не рішення, яке буде працювати в існуючому додатку PHP, і я вважаю, що останній саме те, що шукає ОП та більшість інших. Пояснення також буде корисним для людей, які хочуть зрозуміти підхід.
Шон Бін

1
кожного разу, коли я намагаюся це завжди, розмір переданого файлу становить 50816, але розмір мого файлу більший за цей .. 120 Мб .. Будь-яка ідея, чому це?
Riffaz Starr

set_time_limit (24 * 60 * 60);треба помістити всередину петлі. Це не має ефекту на початку сценарію.
Віктор Джорас

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

Ваша відповідь дуже проста і добре працює, допомогла мені там, де CURL не зміг отримати файл, і це спрацювало. Дякую :)
Tommix

2
Ви можете пояснити, що це насправді робить.
alex

6
Це не стосується проблеми ОП із перевищенням межі пам'яті PHP.
user9645

Це досить просто і прямо. Досить корисно для більш простих випадків, коли файлів мало або навколишнє середовище - це локальна розробка.
Валентина Ши

будь-яка ідея для файлів .xlsx? Він зберігає порожній файл із 0-байтною пам'яттю.
Друв Таккар


8

Використовуйте простий метод в php copy()

copy($source_url, $local_path_with_file_name);

Примітка: якщо файл призначення вже існує, він буде перезаписаний

Функція копіювання PHP ()

Примітка. Для папки призначення потрібно встановити дозвіл 777. Використовуйте цей метод під час завантаження на локальну машину.

Особлива примітка: 777 - це дозвіл в системі Unix, що має повний дозвіл на читання / запис / виконання власника, групи та всіх. Загалом, ми даємо цей дозвіл активам, які не так вже й потрібно, щоб їх приховувати від загальнодоступних веб-серверів. Приклад: папка зображень.


1
Я ніколи ніколи не ставитиму 777 як perms на веб-сервері, і я запускаю будь-якого веб-розробника, у якого погана ідея це зробити. Кожен раз, скрізь. Будь обережний ! Ти не можеш цього зробити ! Подумайте про безпеку. Дотримання правил OWASP недостатньо. Гарне обдумування простих речей має значення.
ThierryB

@ThierryB. Примітка. Я дав місцевий шлях. & це можна використовувати у внутрішніх програмах. Добре читати та розуміти питання та відповідати на них. Придумуйте різні сценарії. І це не прийнята / найкраща відповідь. На кожне запитання є різні відповіді із плюсами та мінусами. Приклад, який ви розумієте: Навіть у Фібоначчі є кілька унікальних рішень, де найкраще буде лише одне. Інші використовуватимуться в різних сценаріях.
Pradeep Kumar Prabaharan

Гаразд, але час для продумання найкращих практик та впровадження їх у захищених місцях дасть вам краще розуміння концепцій, які ви повинні реалізувати. Можливо, якщо зловмисник знаходиться у вашому ($) будинку, роблячи якісь пастки чи будуючи речі найкращим чином, ви можете
завдати

5

Я використовую це для завантаження файлу

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

Рішення PHP 4 і 5:

readfile () не представлятиме жодних проблем із пам'яттю, навіть при надсиланні великих файлів, самостійно. URL-адреса може бути використана як ім'я файлу з цією функцією, якщо обгортки фопенд увімкнено.

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


1
Це не дає відповіді на запитання, оскільки питання стосується запису на диск не до вихідного буфера.
Лоренц Мейер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.