Лаб. работа 6 "Работа с файлами (эмуляция S3-хранилища)"
Тема: Реализация загрузки, хранения и управления файлами с использованием локального хранилища в качестве эмуляции S3.
Цель: Научиться работать с файловой системой Laravel, имитируя облачное хранилище, и реализовать CRUD для мультимедийных данных.
Задачи
- Настроить фейковый S3-диск для локальной разработки.
- Реализовать загрузку файлов к постам.
- Добавить валидацию и оптимизацию файлов.
- Организовать просмотр и удаление файлов.
Методическая часть
1. Настройка файлового хранилища
Добавьте новый диск в
config/filesystems.php:1'disks' => [ 2 // ... другие диски 3 's3-fake' => [ 4 'driver' => 'local', 5 'root' => storage_path('app/s3-fake'), 6 'url' => env('APP_URL') . '/storage/s3-fake', 7 'visibility' => 'public', 8 ], 9],Обновите
.env:1FILESYSTEM_DISK=s3-fakeСоздайте директорию и симлинк:
1mkdir -p storage/app/s3-fake 2php artisan storage:link
2. Модификация модели и миграции
Создайте модель
Media:1php artisan make:model Media -mДобавьте поля в миграцию:
1Schema::create('media', function (Blueprint $table) { 2 $table->id(); 3 $table->string('path'); 4 $table->foreignId('post_id')->constrained()->cascadeOnDelete(); 5 $table->string('type'); // image, video, document 6 $table->timestamps(); 7});Добавьте связь в модели:
- В
Post.php:1public function media() { 2 return $this->hasMany(Media::class); 3}
- В
3. Обновление контроллера PostController
Метод
storeдля загрузки файлов:1use Illuminate\Support\Facades\Storage; 2 3public function store(Request $request) { 4 $validated = $request->validate([ 5 'title' => 'required|max:255', 6 'files.*' => 'file|mimes:jpg,png,pdf,mp4|max:5120', 7 ]); 8 9 $post = Post::create($validated); 10 11 if ($request->hasFile('files')) { 12 foreach ($request->file('files') as $file) { 13 $path = $file->store("posts/{$post->id}", 's3-fake'); 14 Media::create([ 15 'post_id' => $post->id, 16 'path' => $path, 17 'type' => $file->getMimeType(), 18 ]); 19 } 20 } 21 22 return redirect()->route('posts.index'); 23}Метод
destroyFileдля удаления:1public function destroyFile(Media $media) { 2 Storage::disk('s3-fake')->delete($media->path); 3 $media->delete(); 4 return back()->with('success', 'Файл удалён!'); 5}
4. Blade-шаблоны
Форма загрузки файлов (
posts/create.blade.php):1<form method="POST" action="{{ route('posts.store') }}" enctype="multipart/form-data"> 2 @csrf 3 <div class="mb-4"> 4 <label>Прикрепить файлы:</label> 5 <input type="file" name="files[]" multiple class="border p-2 w-full"> 6 @error('files.*') <div class="text-red-500">{{ $message }}</div> @enderror 7 </div> 8 <!-- Остальные поля формы --> 9</form>Галерея файлов (
posts/show.blade.php):1<div class="grid grid-cols-3 gap-4"> 2 @foreach ($post->media as $media) 3 <div class="relative"> 4 @if (Str::startsWith($media->type, 'image')) 5 <img src="{{ Storage::disk('s3-fake')->url($media->path) }}" 6 class="w-full h-48 object-cover"> 7 @else 8 <div class="bg-gray-100 p-4"> 9 <p>{{ basename($media->path) }}</p> 10 </div> 11 @endif 12 <form method="POST" action="{{ route('posts.deleteFile', $media) }}" 13 class="absolute top-2 right-2"> 14 @csrf @method('DELETE') 15 <button type="submit" class="bg-red-500 text-white p-1 rounded">×</button> 16 </form> 17 </div> 18 @endforeach 19</div>
Индивидуальные задания
1. Валидация имен файлов
- Запретите загрузку файлов с пробелами в имени:
1Validator::extend('no_spaces', function ($attr, $value, $params) { 2 return !preg_match('/\s/', $value->getClientOriginalName()); 3}); 4// Использование: 5'files.*' => 'no_spaces',
2. Логирование операций
- Создайте middleware
LogFileOperations:1public function handle($request, Closure $next) { 2 if ($request->hasFile('files')) { 3 \Log::info("Uploaded files: " . json_encode($request->file('files'))); 4 } 5 return $next($request); 6} - Зарегистрируйте его в
bootstrap/app.php:1->withMiddleware(function (Middleware $middleware) { 2 $middleware->append(\App\Http\Middleware\LogFileOperations::class); 3})
3. Оптимизация изображений (опционально)
- Установите пакет:
1composer require spatie/image-optimizer - Оптимизируйте изображения перед сохранением:
1use Spatie\ImageOptimizer\OptimizerChainFactory; 2// ... 3foreach ($request->file('files') as $file) { 4 if (Str::startsWith($file->getMimeType(), 'image')) { 5 $optimizer = OptimizerChainFactory::create(); 6 $optimizer->optimize($file->getRealPath()); 7 } 8 // Сохранение файла... 9}
Проверка работы
Загрузка файлов:
- Откройте форму создания поста.
- Загрузите несколько файлов (изображения, PDF).
- Убедитесь, что файлы сохранились в
storage/app/s3-fake/posts/{post_id}.
Просмотр файлов:
- Откройте страницу поста.
- Проверьте, что изображения отображаются, а PDF показывается как ссылка.
Удаление файлов:
- Нажмите кнопку удаления файла.
- Убедитесь, что файл удалён из хранилища и БД.
Примечания
Преимущества подхода:
- Эмуляция облачного хранилища экономит ресурсы.
- Легко переключиться на реальный S3 через
.env.
Важно:
- Для продакшена замените
s3-fakeнаs3в.env. - Для тестирования используйте
Storage::fake('s3-fake').
- Для продакшена замените