phpdev.org Программисты Laravel, Yii2, Битрикс
  • Поддержка
  • Разработка
  • Кейсы
  • О компании
  • Блог
  • Контакты
  • Галерея
  • Клиентам
  • RU | EN
Главная
Блог
Загрузка больших файлов частями на Laravel
27.01.2022

Загрузка больших файлов частями на Laravel

При загрузке больших файлов на сайт, как правило, необходимо настроить параметры php upload_max_filesize - отвечающий за максимальный размер файла. А что если соединение с интернетом медленное и файл будет загружаться 30 минут? В этом случае так же следует настроить параметр max_execution_time - отвечающий за время выполнения скрипта. А если нет доступа к настройкам php? В этом случае нужно разбить загружаемый файл на части и загружать частями - это и рассмотрим далее. 

Рассмотрим реализацию на фрэймворке Laravel.
Нам понадобятся библиотеки:
- Resumable.js – это библиотека JavaScript, обеспечивающая несколько одновременных, стабильных и возобновляемых загрузок через файловый API HTML5
- https://github.com/pionl/laravel-chunk-upload – библиотека для Laravel для загрузки файлов частями
- jQuery

Для начала скачиваем файл resumable.js из репозитория https://github.com/23/resumable.js/ и поместим его в наш проект /public/js/resumable.js
Затем нам нужно установить библиотеку https://github.com/pionl/laravel-chunk-upload устанавливаем через composer
composer require pion/laravel-chunk-upload

Нет доступа к глобальному composer?

В этом случае необходимо в командной строке запустить команду
php -r "readfile('https://getcomposer.org/installer');" | php
на сервер загрузится файл composer.phar, после этого можем установить необходимую библиотеку используя composer.phar командой
php composer.phar require pion/laravel-chunk-upload

Когда все библиотеки установлены, переходим к созданию контроллера, отвечающего за загрузку. 
Для начала в файл /routes/web.php добавим новые роуты
Route::get('/files-library', [App\Http\Controllers\FilesLibraryController::class, 'filesLibrary'])->name('files-library');
Route::post('files-library/upload', [App\Http\Controllers\UploaderController::class, 'upload'])->name('file-upload');
По пути /files-library - будет доступна страница для загрузки файлов, а files-library/upload - служит для самой загрузки

В папке /app/Http/Controllers создадим новый контроллер FilesLibraryController.php
 // /app/Http/Controllers/FilesLibraryController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class FilesLibraryController extends Controller
{
public function filesLibrary(Request $request) {
return view('/files.library');
}
}

и тут же создадим контроллер загрузчика файлов UploaderController.php  
// /app/Http/Controllers/UploaderController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Pion\Laravel\ChunkUpload\Exceptions\UploadFailedException;

use Storage;
use Illuminate\Http\UploadedFile;
use Pion\Laravel\ChunkUpload\Exceptions\UploadMissingFileException;
use Pion\Laravel\ChunkUpload\Handler\AbstractHandler;
use Pion\Laravel\ChunkUpload\Handler\HandlerFactory;
use Pion\Laravel\ChunkUpload\Receiver\FileReceiver;
use App\Models\Tasks;

class UploaderController extends Controller
{
    public $uploadPath = '/storage/app/public/uploads/';

    /**
     * @param Request $request
     *
     * @return JsonResponse
     *
     * @throws UploadMissingFileException
     * @throws UploadFailedException
     */
    public function upload(Request $request)
    {
        $receiver = new FileReceiver("file", $request, HandlerFactory::classFromRequest($request));

        if ($receiver->isUploaded() === false) {
            throw new UploadMissingFileException();
        }

        $save = $receiver->receive();

        // если загрузка всего файла завершена
        if ($save->isFinished()) {
            return $this->saveFile($save->getFile(), $request);
        }

        // отправляем текущий прогресс о загрузке
        /** @var AbstractHandler $handler */
        $handler = $save->handler();

        return response()->json([
            "done" => $handler->getPercentageDone(),
            'status' => true
        ]);
    }

    /**
     * @param UploadedFile $file
     *
     * @return JsonResponse
     */
    protected function saveFile(UploadedFile $file, Request $request)
    {
        $fileName = $file->getClientOriginalName();
        $file->move($_SERVER['DOCUMENT_ROOT'] . $this->uploadPath, $fileName);

        $filePath = $this->uploadPath . $fileName;

        return response()->json([
            'path' => $filePath,
            'name' => $fileName
        ]);
    }
}
Теперь создадим новый шаблон /resources/views/files.library.blade.php
@extends('layouts.app')

@section('content')
<div class="file-upload-container">
<input type="file" name="file" id="file">
<input type="hidden" name="_token" value="{{ csrf_token() }}">

<div class="action-buttons" style="display: none">
<a href="#" data-action="upload" style="display: none" class="progress-resume-link">Продолжить</a>
<a href="#" data-action="pause" style="display: none" class="progress-pause-link">Пауза</a>
<a href="#" data-action="cancel" class="progress-cancel-link">Отмена</a>
</div>

<ul class="resumable-list"></ul>
</div>
@endsection

@push('javascript')
<script src="{{ asset('js/resumable.js') }}"></script>
<script src="{{ asset('js/script.js') }}"></script>
@endpush


Нам осталось добавить основной js файл /public/js/script.js 
$(document).ready(function () {

var r = new Resumable({
target: '/file-library/upload',
chunkSize: 1 * 1024 * 1024, // 1MB
simultaneousUploads: 3,
testChunks: false,
throttleProgressCallbacks: 1,
query: {_token: $('input[name=_token]').val()}
});

$(document).on('click', '[data-action]', function (e) {
e.preventDefault();
let action = $(this).attr('data-action');

if (action == 'upload') {
r.upload();
} else if (action == 'pause') {
r.pause();
} else if (action == 'cancel') {
r.cancel();
}
})

var fileElement = document.getElementById('file');
r.assignBrowse(fileElement);

r.on('fileAdded', function (file) {
$('.action-buttons').show();
$('.progress-resume-link').hide();
$('.progress-pause-link').show();

$('.resumable-list').append('<li class="resumable-file-' + file.uniqueIdentifier + '">Загрузка <span class="resumable-file-name"></span> <span class="resumable-file-progress"></span>');
$('.resumable-file-' + file.uniqueIdentifier + ' .resumable-file-name').html(file.fileName);
r.upload();
});

r.on('pause', function () {
$('.progress-resume-link').show();
$('.progress-pause-link').hide();
});

r.on('complete', function () {
$('.action-buttons').hide();
$('.resumable-file-' + file.uniqueIdentifier + ' .resumable-file-progress').text('загружено');
});
r.on('fileSuccess', function (file, message) {
$('.resumable-file-' + file.uniqueIdentifier + ' .resumable-file-progress').html('(загружен)');
);
r.on('fileError', function (file, message) {
$('.resumable-file-' + file.uniqueIdentifier + ' .resumable-file-progress').html('(Ошибка: ' + message + ')');
});
r.on('fileProgress', function (file) {
$('.resumable-file-' + file.uniqueIdentifier + ' .resumable-file-progress').html(Math.floor(file.progress() * 100) + '%');
$('.progress-bar').css({width: Math.floor(r.progress() * 100) + '%'});
});
r.on('cancel', function () {
$('.resumable-file-progress').html('отменено');
});
r.on('uploadStart', function () {
$('.progress-resume-link').hide();
$('.progress-pause-link').show();
});
})
Теперь у нас готов базовый функционал загрузки больших файлов с возможностью приостановить или отменить загрузку. 

Все части файлов хранятся в папке /storage/app/chunks, а после того, как все части были загружены - они собираются в один файл и сохраняются в 
 public $uploadPath = '/storage/app/public/uploads/'; 


Возврат к списку


КОНТАКТЫ

Также вы можете связаться с нами по этим контактам:

phpdev.org Программисты Laravel, Yii2, Битрикс
info@phpdev.org
+7 993 898-44-62
+375 44 503 4449
г. Минск, ул. Ольшевского, д. 22, пом. 20
Карта
© 2015 – 2023 PHPDev
Мы в соцсетях:
phpdev.org Программисты Laravel, Yii2, Битрикс
меню
Поддержка Разработка Кейсы О компании Блог Контакты Галерея Клиентам
RU | EN
Программисты Laravel, Yii2, Битрикс
BY +375
  • BY +375
  • RU +7
  • UA +380

Нажимая кнопку «Оставить заявку», Вы даёте согласие на обработку Персональных данных.

phpdev.org Программисты Laravel, Yii2, Битрикс
Спасибо! The application has been successfully sent. We will contact you shortly