В документации по Laravel тут и его английском варианте весьма неявно указано, как производить загрузку внешних файлов.
The put method may be used to store raw file contents on a disk. You may also pass a PHP resource to the
PHPput
method, which will use Flysystem's underlying stream support. Using streams is greatly recommended when dealing with large files:PHPuse Illuminate\Support\Facades\Storage;
Storage::put('file.jpg', $contents);
Storage::put('file.jpg', $resource);
Для записи файла на диск служит метод
PHPput()
. Также вы можете передать в методPHPput()
PHP-resource, чтобы использовать низкоуровневую поддержку потоков Flysystem. Очень рекомендуем использовать потоки при работе с большими файлами:
Однако примеров такого использования не приведено ни в оригинале, ни в переводе статьи.
Между тем такая загрузка весьма проста в использовании:
$resource = \fopen($url, 'r');
$disk = Storage::disk('public');
$disk->put($path, $resource);
\fclose($resource);
Всё было бы просто, но есть подводные камни. Как оказалось, для загрузки самоподписаных сертификатов нужны некоторые танцы с бубном, без которых система будет выдавать нечто вроде
fopen(): SSL operation failed with code 1. OpenSSL Error messages: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
Обратившись к документации по fopen можно найти, что ему необходимо передать контекст
$context = stream_context_create([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
]
]);
$resource = \fopen($url, 'r', false, $context);
$disk = Storage::disk('public');
$disk->put($path, $resource);
\fclose($resource);
Однако при таком запросе можно получить что то типа
failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request
Причина такого поведения кроется в самом ответе от сервера: HTTP 1.1 required
$context = stream_context_create([
'http' => [
'protocol_version' => '1.1',
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
]
]);
$resource = \fopen($url, 'r', false, $context);
$disk = Storage::disk('public');
$disk->put($path, $resource);
\fclose($resource);
Получилось несколько громоздко, однако плюсом такого решения будет автоматическое создание путей $path
Альтернативным решением будет использовать GuzzleHttp
$client = new Client(['verify' => false]);
$client->request('GET', $url, [
'sink' => $path,
]);
Но тогда придется позаботится о создании директорий.
В обоих случаях файл может быть любых размеров, память на него не расходуется.
P.S. Индусам, поставившим self-signed на VSOM в локальной сети уготовлено отдельное место в аду.