• Час читання ~8 хв
  • 30.06.2022

Завантаження файлів у веб-програми – це те, з чим я завжди боровся.

Вони прості, коли ви просто кладете <input type="file" всередину <form> тег і подаєте форму старомодним способом.

Однак ситуація ускладнюється в інтерфейсі, багатому на JavaScript, з поданням форм AJAX.

Livewire JUST додано підтримку завантаження файлів із коробки. Я думаю, що це змінить гру, і не можу дочекатися, щоб розповісти вам все про це!

Давайте пройдемося по двом найпоширенішим стратегіям завантаження файлів (і їх недолікам), а потім подивимося, як Livewire є найкращим з обох світів.

Шлях

подання форми Спочатку вгору, традиційне подання форми. Laravel робить обробку таких завантажень неймовірно простою. Давайте розглянемо з деяким зразком коду:Досвід розробника:


1. Додайте введення файлу до форми, яка публікується на сервері
2. Перевірка та завантаження файлу з контролера на сервері

<form action="/profile" method="POST" type="multipart">
  <input type="text" name="email">
 
    <input type="file" name="photo">
 
    <button>Save</button>
</form>
public function store()
{
    request()->validate([
    'username' => 'required|email',
    'photo' => 'image|max:2000',
  ]);
 
  $filename = request('photo')->store('photos');
 
  Profile::create([
    'username' => request('username'),
    'photo' => $filename,
  ]);
}

Цей підхід простий і зрозумілий, але має кілька недоліків:

  • Файли повинні бути надіслані разом із формою під час надсилання (що робить процес надсилання форми повільним)
  • Файли не можуть бути перевірені з PHP, поки форма не буде надіслана
  • Показати користувачеві попередній перегляд вибраного ними файлу можна зробити лише зі складного JavaScript на інтерфейсі
  • Усі файли повинні проходити через ваш сервер Laravel, перш ніж зберігатися в іншому місці (що може бути зависанням для використання S3 з безсерверними середовищами, такими як Vapor)
  • Оскільки файл не завантажується в окрему, виділену кінцеву точку, бібліотеки завантаження файлів, такі як Filepond, неможливо використовувати.

AJAXy Way

Розглянемо сучасний інтерфейс на основі JS. Більш традиційних форм подань немає. Все обробляється із запитами AJAX на сервер за допомогою чогось на зразок axios або fetch().

На жаль, завантажити файл через AJAX не так просто, як можна було б сподіватися.

Крім того, більшість розробників вирішують використовувати метод під назвою «бічне завантаження» для завантаження файлу ДО подання форми з причин, про які я згадував раніше.

Досвід розробника:

  • Створіть форму з введенням
  • файлу Прослухайте новий файл, вибраний на елементі введення за допомогою JavaScript
  • Негайно завантажте файл у виділену кінцеву точку, тимчасово зберігши його та повернувши ім'я
  • файлу Коли форма надіслана, надішліть уздовж тимчасового імені файлу з формою
  • Обробляйте подання форми, завантаживши тимчасовий файл на основі імені файлу, зберігання його та видалення тимчасового файлу
  • Оскільки деякі файли завантажені, але не надіслані на зберігання, запустіть заплановану команду на очищення незбережених тимчасових файлів через день або близько того

Ось компонент Vue, який я кинув на слух (немає гарантій, що він насправді працює), щоб продемонструвати цю техніку:Тепер давайте подивимося на код на стороні сервера, необхідний для цієї роботи:

<template>
    <form @submit.prevent="save">
      <input v-model="email" type="text" name="email">
    <input @change="updatePhoto" type="file" name="photo">
    <button>Save</button>
  </form>
</template>
 
<script>
    export default {
    data() {
      return {
        email: '',
        photo: '',
        file: null,
      }
    },
 
    methods: {
      updatePhoto(event) {
        let formData = new FormData();
 
        formData.append('file', event.target.files[0]);
 
        axios.post('/file-upload', formData)
            .then(response => {
            this.photo = response.data.filePath
          })
      },
 
      save() {
        axios.post('/profile', {
          email: this.email,
          photo: this.photo,
        }).then(response => {
          ...
        })
      },
    }
  }
</script>

public function handleUpload()
{
  request()->validate(['file' => 'file|max:10000']);
 
  return [
    'file' => request('file')->storeAs('/tmp'),
  ];
}
 
public function handleFormSubmit()
{
  request()->validate([
    'email' => 'required|email',
    'photo' => 'required|string',
  ]);
 
  $tmpFile = Storage::get('/tmp/'.request('photo'));
  Storage::put('/avatars/'.request('photo'), $tmpFile);
  Storage::delete('/tmp/'.request('photo'));
 
  Profile::create([
    'username' => request('username'),
    'photo' => request('photo'),
  ]);
}

Як я вже згадував, ця стратегія називається «бічне завантаження». Це забезпечує всю потужність і гнучкість, які вам можуть знадобитися, але ціною деяких надзвичайної кількості додаткової складності. Часто для чогось такого простого, як дозволити користувачеві завантажити аватар

. Зверніть увагу, що цей код стане НАБАГАТО складнішим, коли ми почнемо додавати такі речі, як перевірка, завантаження спінерів тощо...

Ми можемо зробити краще, ніж це. НАБАГАТО краще.

Спосіб Livewire Оскільки Livewire

базується на JavaScript, ми не можемо просто використовувати традиційні подання форм.

Натомість Livewire використовує «бічне завантаження», але приховує всю складність для вас (з конфігурацією ZERO), надаючи вам досвід традиційної подачі форми, але з кількома оновленнями поганих дуп.

Ось найосновніший приклад:

Досвід

  • розробникаДодайте введений файл до форми (і застосуйтеwire:model="...")
  • Обробляйте надсилання форми за допомогою Livewire, перевіряйте та зберігайте файл, як зазвичай, у контролері
class Profile extends Component
{
  use WithFileUploads;
 
  public $email;
  public $photo;
 
  public function save()
  {
    $this->validate([
      'email' => 'required|email',
      'photo' => 'image|max:2000',
    ]);
 
    $filename = $this->photo->store('photos');
 
    Profile::create([
      'username' => $this->username,
      'photo' => $filename,
    ]);
  }
 
  public function render()
  {
    return view('livewire.profile');
  }
}
<form wire:submit.prevent="save">
  <input wire:model="email" type="text" name="email">
 
    <input wire:model="photo" type="file" name="photo">
 
    <button>Save</button>
</form>

Досить просто, еге ж?

Пам'ятайте, під капотом Livewire фактично «завантажує» файл в тимчасовий каталог.

Однак перевага тут полягає в тому, що вам не потрібно виконувати ЖОДНОЇ роботи.

ВСЕ подбає про вас.

Дозвольте мені проаналізувати всі погані речі, які Livewire дозволяє вам робити з завантаженням файлів.

Обробка кількох завантажень Обробка кількох завантажень

у Livewire - це торт.

Ось він:

<input wire:model=“photos” type=“file” multiple>

Livewire виявить атрибут "кілька" і впорається з усім за вас.

На стороні $this->photos сервера властивість буде масивом завантажених файлів.

Ви можете перевіряти та зберігати їх, як і будь-який інший масив:

...
public $email;
public $photos;
 
public function save()
{
  $this->validate([
    'email' => 'required|email',
    'photos.*' => 'image|max:2000',
  ]);
 
  $filenames = collect($this->photos)->map->store('photos');
 
    Profile::create([
      'username' => $this->username,
      'photos' => $filenames->implode(','),
    ]);
}
...

Показуючи індикатори

завантаження Хочете показати користувачеві індикатор завантаження під час завантаження файлу?

Знову ж таки, впорайтеся з цим, як зазвичай, у Livewire з wire:loading:

<input wire:model=“photo” type=“file”>
 
<div wire:loading wire:target="photo">Uploading...</div>

The "Завантаження ..." Тепер повідомлення відображатиметься протягом усього часу завантаження.

Відображення індикаторів прогресу Простого індикатора

завантаження недостатньо?

Livewire відправляє кілька корисних подій JavaScript, які можна легко зачепити чимось на зразок AlpineJS.

Ось простий індикатор завантаження, написаний альпійським вмістом всередині компонента Livewire:

<div
    x-data="{ progress: 0, uploading: false }"
    @livewire-upload-start="uploading = true"
  @livewire-upload-finish="uploading = false"
  @livewire-upload-error="uploading = false"
  @livewire-upload-progress="progress = $event.detail.progress"
>
  <input wire:model=“photo” type=“file”>
 
  <progress max="100" x-bind:value="progress" x-show="uploading"></progress>
</div>

Перевірка

файлу в режимі реального часу Ви можете перевірити файл, як тільки він буде вибраний, так само, як ви підтвердите БУДЬ-ЯКЕ значення, коли він оновлюється в Livewire.

Зачекайте на "оновлений" життєвий цикл зі своєї властивості файлу, і ви вимкнені!

class Profile extends Component
{
  use WithFileUploads;
 
  public $email;
  public $photo;
 
  // Validate the photo as soon as it's set.
  public function updatedSave()
  {
    $this->validate([
      'photo' => 'image|max:2000',
    ]);
  }
 
  ...
}

Зверніть увагу, ви отримуєте досвід перевірки в режимі реального часу на інтерфейсі, але бекенд-код - це той самий код обробки помилок Laravel, до якого ви звикли:

...
<input wire:model="photo" type="file" name="photo">
 
@error('photo') {{ $message }} @enderror
...

Пряме завантаження на S3

Livewire дозволяє вашим користувачам легко завантажувати файли, які НІКОЛИ насправді не торкаються вашого сервера.

Це НАДЗВИЧАЙНО корисно для великих завантажень або середовищ без сервера, таких як Laravel Vapor.

Вкажіть диск зберігання у своєму додатку, який використовує s3 драйвер пам'яті Laravel, і все буде працювати магічним чином:

config/livewire.php

...
'temporary_file_upload' => [
  'disk' => 's3',
  ...
],
...

Тепер Livewire завантажуватиме тимчасовий файл безпосередньо в S3, використовуючи попередньо підписану URL-адресу завантаження.

Файли зберігатимуться в каталозі, який називається livewire-tmp/ за замовчуванням.

Щоб налаштувати цю папку на видалення файлів старше 24 годин, Livewire надає зручну ремісничу команду:Примітка:

php artisan livewire:configure-s3-upload-cleanup

Ви все ще можете ставитися до властивості як до $this->photo звичайної, фактично не викликаючи S3. Тільки коли ви отримаєте доступ до його вмісту () або його розміру ($this->photo->get();для перевірки: $this->photo->getSize()) буде викликано S3.

Це забезпечує швидку швидкість ваших запитів Livewire для завдань, не пов'язаних із завантаженням.

Тимчасові URL-адреси

попереднього перегляду Іноді ви можете показати своєму користувачеві попередній перегляд щойно вибраного файлу, перш ніж натиснути «надіслати».

Зазвичай це важко, оскільки тимчасові файли завантаження не є загальнодоступними.

Livewire дозволяє надзвичайно легко генерувати тимчасові, безпечні, загальнодоступні URL-адреси для споживання браузером.

Ось приклад вигляду компонента, який відображатиме попередній перегляд зображення щойно завантаженого файлу користувачеві:Тепер, як тільки користувач вибере файл (а після того, як Livewire обробить нове завантаження внутрішньо), він побачить попередній перегляд,

...
@if ($photo)
  <img src="{{ $photo->temporaryUrl() }}">
@endif
 
<input wire:model="photo" type="file" name="photo">
...

перш ніж вони вирішать надіслати форму.

Ще більше S3 Awesomeness

Якщо ви налаштували тимчасовий диск завантаження Livewire на "s3", то ->temporaryUrl() згенеруєте попередньо підписану тимчасову URL-адресу файлу, яка читається безпосередньо з S3.

Тестування завантаження файлів Тестування завантаження

файлів за допомогою Livewire неймовірно просте.

Ви можете використовувати всі звичні функції зі стандартними контролерами Laravel.

Ось приклад тесту, що охоплює завантаження файлів у компоненті:

/** @test **/
function can_upload_photo() {
  Storage::fake();
 
  $file = UploadedFile::fake()->image('avatar.png');
 
  Livewire::test(Profile::class)
    ->set('email', '[email protected]')
    ->set('photo', $file)
    ->call('save');
 
  Storage::assertExists('avatar.png');
}

Інтеграція з FilepondFilepond

- це фантастична бібліотека JavaScript, яка робить перетягування та інші фантазії, пов'язані з завантаженням, надзвичайно легкими.

Функція завантаження файлів Livewire була створена для обслуговування таких інтеграцій.

Якщо вас цікавить, як це виглядає, перейдіть до екранів завантаження файлів Livewire для поглибленого підручника.

Підписання Whew

, яка особливість. Простий на перший погляд, але набагато потужніший, якщо у вас є більш глибокі потреби.

Моя мета з Livewire - зробити веб-розробку в Laravel максимально простою по-людськи (не будучи «ручною»). Сподіваюся, ця особливість перегукується з цією мантрою.

Якщо ви готові зануритися, перейдіть до документації або перегляньте нову серію скрінкастів про завантаження файлів, щоб дізнатися більше!

Приємного завантаження!
–Калеб

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Про автора CrazyBoy49z
WORK EXPERIENCE
Контакти
Ukraine, Lutsk
+380979856297