Хранение и резервные копии

Laravel

2 мин чтения

Laravel работает с объектным хранилищем через встроенный диск s3 фасада Storage. Загруженные файлы, аватары, документы и выгрузки можно хранить в бакете, а не на диске сервера.

Установка

Примеры рассчитаны на Laravel 10 и новее. Установите адаптер S3 для Flysystem:

composer require league/flysystem-aws-s3-v3 "^3.0"

Настройка

Добавьте параметры подключения в .env:

FILESYSTEM_DISK=s3
AWS_ACCESS_KEY_ID=<Access Key>
AWS_SECRET_ACCESS_KEY=<Secret Key>
AWS_DEFAULT_REGION=default
AWS_BUCKET=my-bucket
AWS_ENDPOINT=https://s3.uzcstorage.uz
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_URL=https://s3.uzcstorage.uz/my-bucket

Диск s3 в config/filesystems.php уже читает эти переменные. Добавьте в него две строки, которые отключают лишние заголовки контрольных сумм:

's3' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION'),
    'bucket' => env('AWS_BUCKET'),
    'url' => env('AWS_URL'),
    'endpoint' => env('AWS_ENDPOINT'),
    'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
    'request_checksum_calculation' => 'when_required',
    'response_checksum_validation' => 'when_required',
    'throw' => true,
],

Проверьте подключение в php artisan tinker:

Storage::disk('s3')->put('test.txt', 'Hello, UzCloud!');
Storage::disk('s3')->get('test.txt');

Работа с файлами

use Illuminate\Support\Facades\Storage;

$disk = Storage::disk('s3');

$disk->put('docs/hello.txt', 'Hello, UzCloud!');          // write
$text = $disk->get('docs/hello.txt');                     // read
$exists = $disk->exists('docs/hello.txt');                // check
$disk->copy('docs/hello.txt', 'archive/hello.txt');       // copy
$disk->delete('docs/hello.txt');                          // delete
$files = $disk->files('docs');                            // list a folder
$disk->putFileAs('backups', new \Illuminate\Http\File('/tmp/backup.zip'), 'backup.zip'); // stream a large file

Загрузка файлов из формы

public function store(Request $request)
{
    $request->validate(['avatar' => 'required|image|max:5120']);

    // Saves to s3 under avatars/<random name>.<ext> and returns the path
    $path = $request->file('avatar')->store('avatars', 's3');

    $request->user()->update(['avatar_path' => $path]);
    return back();
}

Ссылки на файлы

По умолчанию файлы закрыты. Для закрытых файлов выдавайте временную ссылку:

$url = Storage::disk('s3')->temporaryUrl($path, now()->addMinutes(30));

// Force download with a file name
$url = Storage::disk('s3')->temporaryUrl($path, now()->addMinutes(30), [
    'ResponseContentDisposition' => 'attachment; filename="report.pdf"',
]);

Для публичных файлов (например, изображений товаров) откройте доступ на чтение к папке политикой бакета и используйте постоянную ссылку:

$url = Storage::disk('s3')->url('public/products/1.jpg');
// https://s3.uzcstorage.uz/my-bucket/public/products/1.jpg

Прямая загрузка из браузера

Большие файлы лучше загружать из браузера прямо в бакет, не пропуская через PHP. Создайте временную ссылку на загрузку методом temporaryUploadUrl (есть в актуальных версиях Laravel; в старых версиях создайте ссылку через SDK, как в статье PHP):

['url' => $url, 'headers' => $headers] = Storage::disk('s3')->temporaryUploadUrl(
    'uploads/' . Str::uuid() . '.mp4', now()->addMinutes(10)
);
return response()->json(compact('url', 'headers'));

Браузер отправляет файл запросом fetch(url, { method: "PUT", headers, body: file }). Настройте для бакета CORS.

Совет

Оставьте диски public и local для временных файлов, а пользовательские файлы перенесите на s3: тогда серверы приложения не хранят состояние, их можно масштабировать и пересоздавать без потери данных.

Совет

См. также: