laravel

Laravel 9 загрузка файлов: как загрузить файл в Laravel

Чтобы загрузить файл в Laravel, используется функция Storage::disk(‘local’)->putFileAs( ‘file name’). Интеграция Laravel Flysystem предоставляет простые драйверы для работы с локальными файловыми системами: SFTP и Amazon S3.

Эти варианты хранения легко переключаются между собой на локальной машине разработки и продакшен сервере, поскольку API остаются одинаковыми для каждой системы.

За исключением бэкенда, мы будем использовать библиотеку dropzone javascript в фронтенде, чтобы перетаскивать файлы в область загрузки.

Но сначала давайте изучим пошаговое руководство по загрузке файла в Laravel.

Шаг 1: Установка Laravel

Введите следующую команду:

laravel new fileupload

Перейдите в каталог проекта и установите зависимости для фронтенда.

npm install

Установите настройки базы данных в файле окружения .env.

Следующим шагом является создание аутентификации с помощью следующей команды.

php artisan make:auth

После этого примените миграций базы данных используя следующую команду.

php artisan migrate

Шаг 2: Создание таблицы миграций.

Нам нужно создать модель File и миграцию к ней используя следующую команду.

php artisan make:model File -m

Определяем схему внутри созданного файла миграций.

// create_files_table

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateFilesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('files', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->index();
            $table->string('title');
            $table->text('overview');
            $table->decimal('price', 6, 2);
            $table->timestamps();
            $table->softDeletes();
            $table->foreign('user_id')
                  ->references('id')
                  ->on('users')
                  ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('files');
    }
}

Теперь, применим миграцию используя следующую команду.

php artisan migrate

Следующий шаг это построение отношений между моделью User и моделью File.

Внутри File.php мы запишем следующий код.

<?php

// File.php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class File extends Model
{
    use SoftDeletes;

    protected $fillable = [
      'title',
      'overview',
      'price'
    ];

    public function user()
    {
      return $this->belongsTo(User::class);
    }
}

Здесь мы имеем состояние, где каждый файл привязан к конкретному пользователю.

Теперь давайте определим отношения внутри User.php.

<?php

// User.php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    public function files()
    {
      return $this->hasMany(File::class);
    }
}

Шаг 3: Создание представлений и маршрутов для загружаемых файлов.

Создадим FileController используя следующую комманду.

php artisan make:controller FileController

Внутри home.blade.php запишем следующий код, со следующим содержанием.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">File Upload</div>
                <div class="card-body">
                <form method="POST" action="{{ route('file.upload') }}" aria-label="{{ __('Upload') }}">
                        @csrf
                        <div class="form-group row">
                            <label for="title" class="col-sm-4 col-form-label text-md-right">{{ __('Title') }}</label>
                            <div class="col-md-6">
                                <input id="title" type="text" class="form-control{{ $errors->has('title') ? ' is-invalid' : '' }}" name="title" value="{{ old('title') }}" required autofocus />
                                @if ($errors->has('title'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('title') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="overview" class="col-sm-4 col-form-label text-md-right">{{ __('Overview') }}</label>
                            <div class="col-md-6">
                                <textarea id="overview" cols="10" rows="10" class="form-control{{ $errors->has('overview') ? ' is-invalid' : '' }}" name="overview" value="{{ old('overview') }}" required autofocus></textarea>
                                @if ($errors->has('overview'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('overview') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="price" class="col-md-4 col-form-label text-md-right">{{ __('Price') }}</label>
                            <div class="col-md-6">
                                <input id="price" type="text" class="form-control{{ $errors->has('price') ? ' is-invalid' : '' }}" name="price" required>
                                @if ($errors->has('price'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('price') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Upload') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Также, необходимо определить маршруты для Post в файле routes >> web.php.

<?php

// web.php

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Route::post('file/upload', 'FileController@store')->name('file.upload');

Теперь сохраняя файл и регистрируя нового пользователя, вы можете увидеть такую страницу.

Вы можете увидеть, что мы используем еще не все поля формы для файла. Мы сделаем это через минуту, но это наша основная настройка формы.

Шаг 4: Сохранение данных формы.

Нам необходимо записать функцию store для сохранения данных формы в базу данных.

<?php

// FileController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class FileController extends Controller
{
    public function store(Request $request)
    {
       $request->validate([
        'title' => 'required:max:255',
        'overview' => 'required',
        'price' => 'required|numeric'
      ]);

      auth()->user()->files()->create([
        'title' => $request->get('title'),
        'overview' => $request->get('overview'),
        'price' => $request->get('price')
      ]);

      return back()->with('message', 'Your file is submitted Successfully');
    }
}

Здесь используется метод validate, поставляемый объектом Illuminate\Http\Request. При соблюдений правил валидации ваш код продолжит нормально выполняться. Но, если проверка не пройдена, будет сгенерировано исключение Illuminate\Validation\ValidationException и ответ об ошибке будет автоматически отправлен обратно пользователю. Мы также включили проверку для наших полей формы.

Далее, нужно обновить представление update для для показа flash сообщений.

// home.blade.php 

@if(session()->get('message'))
    <div class="alert alert-success">
       {{ session()->get('message') }}
    </div>
@endif

Шаг 5: Установка отношений файлов загрузки.

Давайте создадим еще одну модель и миграцию к ней для загруженных файлов.

Введите следующую команду для генераций модели и миграций.

php artisan make:model Upload -m

Запишите следующую схему в файле <timestamp>create_uploads_table.php

public function up()
{
        Schema::create('uploads', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->index();
            $table->string('filename');
            $table->softDeletes();
            $table->timestamps();

            $table->foreign('user_id')
                  ->references('id')
                  ->on('users')
                  ->onDelete('cascade');
        });
}

Примените миграций к базе данных используя следующую команду.

php artisan migrate

Теперь нам нужно установить отношения.

Внутри файла Upload.php запишите следующий код.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Upload extends Model
{

  protected $fillable = [
    'filename'
  ];

    public function user()
    {
      return $this->belongsTo(User::class);
    }
}

Здесь модель Upload использует belongsTo для File и User.

Запишите следующие методы отношений для модели в файле User.php.

public function files()
{
   return $this->hasMany(File::class);
}

public function uploads()
{
   return $this->hasMany(Upload::class);
}

В заключений, запишите следующие отношения в файле модели File.php.

public function user()
{
   return $this->belongsTo(User::class);
}

Шаг 6: Настройка Dropzone.

Установите библиотеку Dropzone используя следующую команду

npm install dropzone --save

Подключите эту библиотеку внутри файла resources>>assets>>js>>bootstrap.js

Нам нужно добавить следующий код в этот файл.

/**
 * Dropzone
 */

 window.Dropzone = require('dropzone');
 Dropzone.autoDiscover = false;

Импортируем файлы css относящиеся к Dropzone. Также внутри файла resources>>assets>>sass>>app.scss добавим следующее

// Fonts
@import url('https://fonts.googleapis.com/css?family=Nunito');

// Variables
@import 'variables';

// Bootstrap
@import '~bootstrap/scss/bootstrap';

// Dropzone
@import '~dropzone/dist/min/dropzone.min.css';

.dropzone {
  margin-bottom: 20px;
  min-height: auto;
}

Скомпилируем эти файлы следующей командой.

npm run dev

Мы будем использовать библиотеку Dropzone в файле home.blade.php следующем образом.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            @if(session()->get('message'))
            <div class="alert alert-success">
              {{ session()->get('message') }}
            </div>
            @endif
            <div class="card">
                <div class="card-header">File Upload</div>
                <div class="card-body">
                <form method="POST" action="{{ route('file.upload') }}" aria-label="{{ __('Upload') }}">
                      @csrf
                      <div class="form-group row ">
                        <label for="title" class="col-sm-4 col-form-label text-md-right">{{ __('File Upload') }}</label>
                        <div class="col-md-6">
                          <div id="file" class="dropzone"></div>
                        </div>    
                      </div>
                        <div class="form-group row">
                            <label for="title" class="col-sm-4 col-form-label text-md-right">{{ __('Title') }}</label>
                            <div class="col-md-6">
                                <input id="title" type="text" class="form-control{{ $errors->has('title') ? ' is-invalid' : '' }}" name="title" value="{{ old('title') }}" required autofocus />
                                @if ($errors->has('title'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('title') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group row">
                            <label for="overview" class="col-sm-4 col-form-label text-md-right">{{ __('Overview') }}</label>
                            <div class="col-md-6">
                                <textarea id="overview" cols="10" rows="10" class="form-control{{ $errors->has('overview') ? ' is-invalid' : '' }}" name="overview" value="{{ old('overview') }}" required autofocus></textarea>
                                @if ($errors->has('overview'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('overview') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="price" class="col-md-4 col-form-label text-md-right">{{ __('Price') }}</label>
                            <div class="col-md-6">
                                <input id="price" type="text" class="form-control{{ $errors->has('price') ? ' is-invalid' : '' }}" name="price" required>
                                @if ($errors->has('price'))
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $errors->first('price') }}</strong>
                                    </span>
                                @endif
                            </div>
                        </div>
                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Upload') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

@section('scripts')
  <script>
    var drop = new Dropzone('#file', {
      url: "{{ route('upload') }}"
    });
  </script>
@endsection

Также определим POST маршрут загрузки файлов.

Route::post('upload', 'FileController@upload')->name('upload');

Создайте функцию загрузки внутри файла FileController.php.

public function upload()
{
   return 'x';
}

Сохраните изменения и перейдите по адресу похожему на http://fileupload.test/home.

Шаг 7: Обработка процесса загрузки файла.

Первое что нам необходимо сделать, это задать заголовки во время отправки файла POST запросом на сервер.

@section('scripts')
  <script>
    var drop = new Dropzone('#file', {
      createImageThumbnails: false,
      addRemoveLinks: true,
      url: "{{ route('upload') }}",
      headers: {
        'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content
      }
    });
  </script>
@endsection

Заключительный код для файла FileController.php будет похож на этот.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Upload;

class FileController extends Controller
{
    public function store(Request $request)
    {
       $request->validate([
        'title' => 'required:max:255',
        'overview' => 'required',
        'price' => 'required|numeric'
      ]);

      auth()->user()->files()->create([
        'title' => $request->get('title'),
        'overview' => $request->get('overview'),
        'price' => $request->get('price')
      ]);

      return back()->with('message', 'Your file is submitted Successfully');
    }

    public function upload(Request $request)
    {
      $uploadedFile = $request->file('file');
      $filename = time().$uploadedFile->getClientOriginalName();

      Storage::disk('local')->putFileAs(
        'files/'.$filename,
        $uploadedFile,
        $filename
      );

      $upload = new Upload;
      $upload->filename = $filename;

      $upload->user()->associate(auth()->user());

      $upload->save();

      return response()->json([
        'id' => $upload->id
      ]);
    }
}

Вы можете увидеть загруженный файл внутри каталога Storage>>app>>files

To top