Загрузка файлов в Laravel: короткое руководство
От формы до сохранённого файла на диске за один вечер. Разбираем валидацию, хранение через Storage и раздачу файлов на Laravel 11.x без лишней теории.

К концу этого руководства у вас будет форма, которая принимает файл, проверяет его размер и тип, сохраняет на диск и возвращает ссылку. На всё уйдёт около 30–40 минут, если Laravel уже установлен. Пишу для Laravel 11.x, но фасад Storage и метод store() работают так же в 10.x и большинстве 9.x.
Что нужно иметь до начала
- Установленный Laravel 11.x (
composer create-project laravel/laravel example-app) и работающийphp artisan serve. - PHP 8.2 или выше — это требование Laravel 11.
- Настроенный диск в
config/filesystems.php. По умолчанию есть дискlocal(папкаstorage/app/private) иpublic(папкаstorage/app/public). Начнём сpublic, потому что его файлы можно раздавать по прямой ссылке. - Права на запись в
storage/. Если получите ошибку прав на шаге сохранения — вернитесь сюда.
Проверьте в php.ini значения upload_max_filesize и post_max_size. Если файл больше этих лимитов, PHP отбросит его ещё до того, как запрос дойдёт до Laravel, и валидация ничего не поймает.
Шаг 1. Форма для загрузки
Форма загрузки файла обязана иметь атрибут enctype="multipart/form-data". Без него браузер отправит только имя файла, а не его содержимое, и на сервере вы получите пустоту.
<!-- resources/views/upload.blade.php -->
<form method="POST" action="/upload" enctype="multipart/form-data">
@csrf
<input type="file" name="document">
<button type="submit">Загрузить</button>
</form>
@if (session('uploaded'))
<p>Файл сохранён: {{ session('uploaded') }}</p>
@endif
Директива @csrf обязательна: без токена Laravel вернёт ошибку 419. Проверка: откройте страницу в браузере, вы должны увидеть поле выбора файла и кнопку.
Шаг 2. Маршруты
// routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UploadController;
Route::get('/upload', [UploadController::class, 'show']);
Route::post('/upload', [UploadController::class, 'store']);
Проверка: команда php artisan route:list должна показать обе строки с путём upload.
Шаг 3. Контроллер с валидацией и сохранением
Создайте контроллер: php artisan make:controller UploadController. Валидация здесь не украшение — без неё пользователь загрузит 500-мегабайтный архив или PHP-скрипт под видом картинки.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UploadController extends Controller
{
public function show()
{
return view('upload');
}
public function store(Request $request)
{
// max указывается в килобайтах: 2048 = 2 МБ
$request->validate([
'document' => ['required', 'file', 'mimes:pdf,jpg,png', 'max:2048'],
]);
// store() сам сгенерирует уникальное имя и вернёт путь
$path = $request->file('document')->store('documents', 'public');
return back()->with('uploaded', $path);
}
}
Метод store('documents', 'public') кладёт файл в storage/app/public/documents и возвращает относительный путь вроде documents/9xKk2...jpg. Имя генерируется случайное — это защищает от перезаписи чужих файлов и от подстановки опасного имени.
Проверка: выберите PDF или картинку до 2 МБ, отправьте форму. Под формой появится строка «Файл сохранён: documents/...». Файл должен физически лежать в storage/app/public/documents.
Почему mimes, а не просто расширение
Правило mimes проверяет реальный MIME-тип файла по его содержимому, а не по расширению в имени. Переименованный virus.php в photo.jpg не пройдёт, если внутри не картинка.
Шаг 4. Публичная ссылка на файл
Файлы из диска public недоступны по прямой ссылке, пока вы не создадите символьную ссылку из public/storage в storage/app/public:
php artisan storage:link
После этого получить URL в шаблоне можно так:
<a href="{{ Storage::url($path) }}">Скачать</a>
<!-- вернёт /storage/documents/9xKk2...jpg -->
Проверка: откройте выданную ссылку в браузере — файл должен открыться или скачаться. Если получаете 404, символьная ссылка не создана или указывает не туда.
Дискpublic— это про «файл виден всем по ссылке». Аватарки и обложки товаров туда можно. Паспорта, договоры и медсправки — нельзя: держите их на дискеlocalи отдавайте через контроллер с проверкой прав.
Приватные файлы
Если файл не должен быть доступен по прямому URL, сохраняйте его на диск по умолчанию (local) и отдавайте через отдельный маршрут, проверив права доступа:
// сохранение на приватный диск
$path = $request->file('document')->store('private/docs');
// раздача через контроллер
public function download(string $file)
{
// здесь проверьте, что текущий пользователь имеет право на файл
abort_unless(auth()->check(), 403);
return response()->download(storage_path('app/' . $file));
}
Так файл не лежит в веб-доступной папке, и никто не скачает его, зная путь.
Если не получилось
- 419 Page Expired — забыли
@csrfв форме или сессия протухла. Добавьте директиву, обновите страницу. - The document failed to upload или пустой
$request->file()— на форме нетenctype="multipart/form-data", либо файл большеupload_max_filesizeвphp.ini. - The document must be a file of type: pdf, jpg, png — реальный тип файла не совпал с расширением, либо расширение не в списке
mimes. - The document must not be greater than 2048 kilobytes — файл больше лимита в правиле
max. Правило считает килобайты, а не мегабайты. - 404 по ссылке /storage/... — не выполнена команда
php artisan storage:link, либо на shared-хостинге симлинки запрещены.
Что делать дальше
Базовая загрузка работает. Дальше по мере надобности:
- Вынести правила валидации в отдельный
FormRequest(php artisan make:request UploadRequest) — контроллер станет чище. - Настроить диск
s3вconfig/filesystems.php, если файлов много: код сохранения при этом не меняется, меняется только имя диска. - Для нескольких файлов используйте
name="documents[]"иmultipleв форме, а в контроллере — цикл по$request->file('documents').
Обработка изображений (ресайз, превью) — отдельная тема, здесь мы её не трогали. Точный список доступных методов Storage и правил валидации сверяйте с документацией вашей версии Laravel.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: Laravel 11 — File Storage, Laravel 11 — Validation (available rules)
Частые вопросы
А если у меня Laravel 10 или 9?
Файл не загружается, а ошибки валидации нет — в чём дело?
Правило max:2048 — это два мегабайта или два килобайта?
storage:link не работает на моём хостинге
Как безопасно хранить документы, которые не должны видеть посторонние?
Как проверить реальный тип файла, а не расширение?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.