Скачивание данных из интернета: работа с файлами в PHP

Раздел: Работа с файлами в PHP -> Сетевые запросы в PHP

Способы загрузки удаленных файлов в PHP

Наиболее эффективное решение использование расширения cURL. Оно позволяет контролировать таймауты, редиректы, SSL, а также записывать данные напрямую в файл, избегая загрузки всего содержимого в память.

<?php
$url = 'https://example.com/largefile.zip';
$destination = '/var/www/downloads/file.zip';

$ch = curl_init($url);
$fp = fopen($destination, 'wb');

curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);

if (curl_exec($ch) === false) {
    $error = curl_error($ch);
    // обработка ошибки
} else {
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($httpCode != 200) {
        // не успешный код
    }
}

curl_close($ch);
fclose($fp);

Php скачать файл по ссылке (скачивание файла по ссылке через php)

Пояснение: инициализация сеанса cURL, открытие файла на бинарную запись, привязка потока вывода к файлу, отключение заголовков, разрешение следования редиректам, установка таймаута, выполнение запроса, проверка на ошибки и HTTP код, закрытие ресурсов.

Как скачать файл с помощью file_get_contents и file_put_contents?

Этот вариант подходит для небольших файлов, если в php.ini включена опция allow_url_fopen. Весь файл загружается в память, что может привести к превышению memory_limit.

<?php
$url = 'https://example.com/smallfile.txt';
$content = file_get_contents($url);
if ($content !== false) {
    file_put_contents('/path/to/save.txt', $content);
} else {
    // ошибка
}

Проблема: при отключенном allow_url_fopen file_get_contents возвращает false. Решение - использовать cURL.

Как использовать функцию copy() для загрузки файла по URL?

copy() также полагается на allow_url_fopen. Она копирует содержимое из одного потока в другой, но не даёт контроля над процессом.

<?php
$source = 'https://example.com/file.pdf';
$dest = '/local/file.pdf';
if (copy($source, $dest)) {
    echo 'Файл скопирован';
} else {
    echo 'Ошибка копирования';
}

Как выполнить пошаговую запись через fopen и fwrite?

Этот метод экономит память, так как данные читаются по частям. Используется удалённый поток с префиксом http:// или https://, для чего требуется allow_url_fopen.

<?php
$src = fopen('https://example.com/video.mp4', 'rb');
$dst = fopen('/local/video.mp4', 'wb');
if ($src && $dst) {
    while (!feof($src)) {
        $chunk = fread($src, 8192);
        if ($chunk === false) break;
        fwrite($dst, $chunk);
    }
    fclose($src);
    fclose($dst);
    echo 'Загрузка завершена';
} else {
    echo 'Не удалось открыть потоки';
}

Проблема: если файл передаётся с использованием Transfer-Encoding: chunked, некоторые версии PHP могут работать некорректно. Рекомендуется cURL.

Как организовать низкоуровневое скачивание через fsockopen?

fsockopen позволяет вручную отправить HTTP-запрос и обработать ответ. Требуется самостоятельный разбор заголовков и тела. Применяется, когда нет доступа к cURL и отключен allow_url_fopen.

<?php
$host = 'example.com';
$port = 443;
$path = '/file.zip';
$fp = fsockopen('ssl://'.$host, $port, $errno, $errstr, 30);
if (!$fp) {
    echo 'Ошибка: '.$errstr;
} else {
    $crlf = chr(13).chr(10);
    $request = 'GET '.$path.' HTTP/1.1'.$crlf;
    $request .= 'Host: '.$host.$crlf;
    $request .= 'Connection: close'.$crlf.$crlf;
    fwrite($fp, $request);
    // пропустить заголовки
    while (!feof($fp)) {
        $line = fgets($fp);
        if ($line === $crlf) break;
    }
    // запись тела в файл
    $dest = fopen('/local/file.zip', 'wb');
    while (!feof($fp)) {
        $data = fread($fp, 4096);
        if ($data === false) break;
        fwrite($dest, $data);
    }
    fclose($dest);
    fclose($fp);
    echo 'Готово';
}

Проблема: сложность обработки chunked encoding и перенаправлений. Для продакшена лучше использовать cURL.

Как применить библиотеку Guzzle для загрузки файла?

Guzzle - мощный HTTP-клиент для PHP. Позволяет легко скачивать файлы с сохранением на диск, поддерживает параллельные запросы, middleware, пулы подключений.

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Psr7;

$client = new Client();
$response = $client->get('https://example.com/document.pdf', [
    'sink' => '/local/document.pdf'
]);
echo $response->getStatusCode();

Параметр sink указывает путь для сохранения ответа непосредственно в файл. Подходит для больших объёмов.

Общие проблемы и их решения

  • allow_url_fopen отключен - используйте cURL или fsockopen.
  • Превышение memory_limit - не используйте file_get_contents для больших файлов, применяйте потоковую запись.
  • Таймауты соединения или выполнения - установите соответствующие опции CURLOPT_TIMEOUT, CURLOPT_CONNECTTIMEOUT, а также set_time_limit(0).
  • SSL ошибки - временно отключите проверку CURLOPT_SSL_VERIFYPEER=0, в production используйте корректный CA bundle.
  • Редиректы - cURL с CURLOPT_FOLLOWLOCATION=true; при ручном разборе заголовков обрабатывайте Location.
  • Прокси-сервер - задайте CURLOPT_PROXY и CURLOPT_PROXYPORT.

Расширенные примеры скачивания файлов

Скачивание с отображением прогресса (cURL)

Функция CURLOPT_PROGRESSFUNCTION позволяет отслеживать процесс загрузки. Установите CURLOPT_NOPROGRESS в false.

Пример
<?php
$url = 'https://example.com/bigfile.iso';
$dest = '/tmp/bigfile.iso';
$fp = fopen($dest, 'wb');
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function($resource, $download_size, $downloaded, $upload_size, $uploaded) {
    if ($download_size > 0) {
        $percent = round($downloaded / $download_size * 100, 2);
        echo chr(13).'Прогресс: '.$percent.'%';
        flush();
    }
});
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
fclose($fp);
if (!$error) echo PHP_EOL.'Загрузка завершена';
Прогресс: 12.45%
Прогресс: 45.67%
...
Прогресс: 100%
Загрузка завершена

Докачка файла с использованием HTTP Range

Отправляется заголовок Range с указанием байтового смещения, чтобы продолжить загрузку с места остановки.

Пример
<?php
$url = 'https://example.com/partial.zip';
$dest = '/tmp/partial.zip';
$mode = 'wb';
$existingSize = 0;
if (file_exists($dest)) {
    $existingSize = filesize($dest);
    $mode = 'ab'; // append
}
$ch = curl_init($url);
$fp = fopen($dest, $mode);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Range: bytes='.$existingSize.'-']);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode == 206 || $httpCode == 200) {
    echo 'Загружено с позиции '.$existingSize;
} else {
    echo 'Ошибка: HTTP '.$httpCode;
}
curl_close($ch);
fclose($fp);
Загружено с позиции 5242880

Асинхронная загрузка нескольких файлов через curl_multi

С помощью curl_multi инициализируются несколько одновременных запросов, что ускоряет массовое скачивание.

Пример
<?php
$urls = [
    'https://example.com/file1.zip',
    'https://example.com/file2.zip',
    'https://example.com/file3.zip'
];
$mh = curl_multi_init();
$handles = [];
foreach ($urls as $i => $url) {
    $fp = fopen('/tmp/file'.$i.'.zip', 'wb');
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_FILE, $fp);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_multi_add_handle($mh, $ch);
    $handles[(int)$ch] = ['ch' => $ch, 'fp' => $fp, 'url' => $url];
}
$active = null;
do {
    $status = curl_multi_exec($mh, $active);
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
foreach ($handles as $data) {
    $error = curl_error($data['ch']);
    if ($error) echo 'Ошибка '.$data['url'].': '.$error.PHP_EOL;
    curl_multi_remove_handle($mh, $data['ch']);
    fclose($data['fp']);
}
curl_multi_close($mh);
echo 'Все файлы загружены';
Все файлы загружены

Скачивание через прокси-сервер с аутентификацией

Указывается CURLOPT_PROXY с полным URI, включая логин и пароль.

Пример
<?php
$url = 'https://example.com/report.pdf';
$proxy = 'http://user:pass@proxy.example.com:8080';
$dest = '/tmp/report.pdf';
$ch = curl_init($url);
$fp = fopen($dest, 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_PROXY, $proxy);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_exec($ch);
if (curl_errno($ch)) {
    echo 'Ошибка: '.curl_error($ch);
} else {
    echo 'Файл сохранен';
}
curl_close($ch);
fclose($fp);
Файл сохранен

Скачивание файла по ссылке через PHP - comments

En
Php скачать файл по ссылке (php)