Чтобы загрузить файл в 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