Скачивание данных из интернета: работа с файлами в 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);
Файл сохранен