Иногда для загрузки больших файлов стандартной формы HTML становится недостаточно, по той причине, что на стороне веб-сервера имеются ограничения по размеру загружаемого файла. Иногда разработчики (особенно начинающие) приходят к тому что просто меняют лимиты загрузки файлов на сервер в большую сторону, но не всё так просто. Увеличение лимита загружаемых файлов тянут за собой другие проблемы, которые вы создаёте для вашего веб-сервера. Об этом много информации на просторах интернета.
Мы же поговорим о другом способе загрузки файлов, более правильном и безопасным. В этих случаях остаётся один и скорее единственный правильный способ – это разбиение файла на части.
Так как передача файла происходит с клиентской части, то логично предположить, что разбивать и передавать частями нужно на клиентской стороне. Для этих целей мы можем воспользоваться готовым решением под названием resumable.js. Данный плагин поможет реализовать отправку на сервер файла с вашей формы частями.
Вы можете установить resumable.js с GIT, либо подключить его напрямую через CDN.
https://cdn.jsdelivr.net/npm/resumablejs@1.1.0/resumable.min.js
Следующим шагом является подготовка HTML формы для выбора файла. Для примера мы сделаем такую разметку:
<div class="container pt-4"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header text-center"> <h5>Загрузка файла</h5> </div> <div class="card-body"> <div id="upload-container" class="text-center"> <button id="browseFile" class="btn btn-primary">Выбрать файл</button> </div> <div style="display: none" class="progress mt-3" style="height: 25px"> <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100" style="width: 75%; height: 100%">75%</div> </div> </div> <div class="card-footer p-4" style="display: none"> <video id="videoPreview" src="" controls style="width: 100%; height: auto"></video> </div> </div> </div> </div> </div>
Разметка сделана для демонстрации примера, разумеется, вы её будете адаптировать под свой проект. Теперь что касается обработчика на JavaScript, инициализация плагина производится так же для демонстрации. Полный набор параметров и триггеров вы можете посмотреть в описании в Git репозитории проекта.
<script type="text/javascript"> let browseFile = $('#browseFile'); let resumable = new Resumable({ target: '{{ route('files.upload.large') }}', query:{_token:'{{ csrf_token() }}'}, // CSRF token fileType: ['mp4'], chunkSize: 10*1024*1024, // default is 1*1024*1024, this should be less than your maximum limit in php.ini headers: { 'Accept' : 'application/json' }, testChunks: false, throttleProgressCallbacks: 1, }); resumable.assignBrowse(browseFile[0]); resumable.on('fileAdded', function (file) { // trigger when file picked showProgress(); resumable.upload() // to actually start uploading. }); resumable.on('fileProgress', function (file) { // trigger when file progress update updateProgress(Math.floor(file.progress() * 100)); }); resumable.on('fileSuccess', function (file, response) { // trigger when file upload complete response = JSON.parse(response) $('#videoPreview').attr('src', response.path); $('.card-footer').show(); }); resumable.on('fileError', function (file, response) { // trigger when there is any error alert('file uploading error.') }); let progress = $('.progress'); function showProgress() { progress.find('.progress-bar').css('width', '0%'); progress.find('.progress-bar').html('0%'); progress.find('.progress-bar').removeClass('bg-success'); progress.show(); } function updateProgress(value) { progress.find('.progress-bar').css('width', `${value}%`) progress.find('.progress-bar').html(`${value}%`) } function hideProgress() { progress.hide(); } </script>
Теперь что касается серверной части, то там нужно реализовать обработку передачи файлов частями. Для этих целей в Laravel так же есть пакет под названием laravel-chunk-upload который вы можете установить его себе с Git-репозитория.
Установка laravel-chunk-upload в ваш проект через composer
composer require pion/laravel-chunk-upload
Если возникают проблемы с установкой пакета в Laravel можно поступить другим путём.
Далее пишем обработчик формы для загрузки файла в контроллере.
<?php public function uploadLargeFiles(Request $request) { $receiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request)); if (!$receiver->isUploaded()) { // file not uploaded } $fileReceived = $receiver->receive(); // receive file if ($fileReceived->isFinished()) { // file uploading is complete / all chunks are uploaded $file = $fileReceived->getFile(); // get file $extension = $file->getClientOriginalExtension(); $fileName = str_replace('.'.$extension, '', $file->getClientOriginalName()); //file name without extenstion $fileName .= '_' . md5(time()) . '.' . $extension; // a unique file name $disk = Storage::disk(config('filesystems.default')); $path = $disk->putFileAs('videos', $file, $fileName); // delete chunked file unlink($file->getPathname()); return [ 'path' => asset('storage/' . $path), 'filename' => $fileName ]; } // otherwise return percentage information $handler = $fileReceived->handler(); return [ 'done' => $handler->getPercentageDone(), 'status' => true ]; }
Базовый минимум для загрузки файлов реализован в обработчике. Модифицировать его мы можете конечно же в своём проекте так как вам нужно.