• Czas czytania ~8 min
  • 30.06.2022

Przesyłanie plików w aplikacjach internetowych to coś, z czym zawsze się zmagałem.

Są łatwe, gdy po prostu umieszczasz <input type="file" wewnątrz <form> tagu i przesyłasz formularz w staromodny sposób.

Jednak sprawy stają się trudniejsze w bogatym w JavaScript interfejsie z przesyłaniem formularzy AJAX.

Livewire JUST dodał obsługę przesyłania plików po wyjęciu z pudełka. Myślę, że to będzie przełom i nie mogę się doczekać, aby wam o tym opowiedzieć!

Przyjrzyjmy się dwóm najpopularniejszym strategiom przesyłania plików (i ich niedociągnięciom), a następnie zobaczymy, jak Livewire jest najlepszy z obu światów.

Formularz Submit Way Na

początek, tradycyjne przesyłanie formularza. Laravel sprawia, że obsługa tego rodzaju przesyłania jest niezwykle prosta. Przyjrzyjmy się przykładowemu kodowi:The Developer Experience:


1. Dodaj plik wejściowy do formularza, który ogłasza się na serwerze
2. Sprawdź poprawność i prześlij plik z kontrolera na serwerze

<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,
  ]);
}

Takie podejście jest proste i bezpośrednie, ale ma kilka wad:

  • Pliki muszą być przesyłane wraz z formularzem podczas przesyłania (co spowalnia przesyłanie formularza)
  • Pliki nie mogą być weryfikowane z PHP, dopóki formularz nie zostanie przesłany
  • Pokazanie użytkownikowi podglądu wybranego pliku można wykonać tylko ze złożonego JavaScript na froncie
  • Wszystkie pliki muszą przejść przez serwer Laravel, zanim zostaną zapisane w innym miejscu (co może być utrudnieniem dla korzystania z S3 w środowiskach bezserwerowych, takich jak Vapor)Ponieważ
  • plik nie jest przesyłany do oddzielnego, dedykowanego punktu końcowego, biblioteki przesyłania plików, takie jak Filepond, są niemożliwe do użycia.

Sposób

AJAXy Rozważ nowoczesny, oparty na JS front-end. Nie ma już tradycyjnych formularzy. Wszystko jest obsługiwane za pomocą żądań AJAX do serwera za pomocą czegoś takiego jak axios lub fetch().

Niestety, przesyłanie pliku przez AJAX nie jest tak proste, jak można by się spodziewać.

Ponadto większość programistów decyduje się na użycie techniki zwanej "side-loading", aby przesłać plik PRZED przesłaniem formularza z powodów, o których wspomniałem wcześniej.

Doświadczenie programisty:

  • Tworzenie formularza z danymi wejściowymi do pliku Nasłuchiwanie nowego pliku zaznaczonego w elemencie wejściowym
  • za pomocą języka JavaScript
  • Natychmiast prześlij plik do dedykowanego punktu końcowego, tymczasowo przechowując go i zwracając nazwę pliku Po przesłaniu formularza wyślij tymczasową nazwę
  • pliku wraz z formularzem Obsługa przesyłania formularza
  • , ładując plik tymczasowy na podstawie nazwy pliku, przechowywanie go i usuwanie pliku
  • tymczasowego Ponieważ niektóre pliki są przekazywane, ale nie są przesyłane do przechowywania, uruchom zaplanowane polecenie, aby wyczyścić niezapisane pliki tymczasowe po około

dniu Oto komponent Vue, który wrzuciłem do zespołu przez ucho (nie ma gwarancji, że faktycznie działa), aby zademonstrować tę technikę:Teraz spójrzmy na kod po stronie serwera wymagany do tego, aby to działało:

<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'),
  ]);
}

Jak wspomniałem, ta strategia nazywa się "side-loading". Pozwala na całą moc i elastyczność, jakiej możesz chcieć, ale kosztem ekstremalnych ilości dodatkowej złożoności. Często w przypadku czegoś tak prostego, jak umożliwienie użytkownikowi przesłania awatara.

Zauważ, że ten kod stanie się ZNACZNIE bardziej złożony, gdy zaczniemy dodawać takie rzeczy jak walidacja, ładowanie spinnerów itp.

Stać nas na więcej. ZNACZNIE lepiej.

Sposób

Livewire Ponieważ Livewire jest oparty na JavaScript, nie możemy po prostu używać tradycyjnych formularzy.

Zamiast tego Livewire używa "ładowania bocznego", ale ukrywa całą złożoność (z konfiguracją ZERO), zapewniając doświadczenie tradycyjnego przesyłania formularzy, ale z garścią złych aktualizacji.

Oto najbardziej podstawowy przykład:

Środowisko

  • programistyczneDodawanie pliku wejściowego do formularza (i stosowanie wire:model="...")
  • Obsługa przesyłania formularza za pomocą Livewire oraz sprawdzanie poprawności i przechowywanie pliku w normalny sposób w kontrolerze
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>

Całkiem proste, co?

Pamiętaj, że pod maską Livewire faktycznie "ładuje" plik do katalogu tymczasowego.

Korzyścią jest jednak to, że nie musisz wykonywać ŻADNEJ pracy.

WSZYSTKO jest załatwione za Ciebie.

Pozwól mi przebić się przez wszystkie złe rzeczy, które Livewire pozwala zrobić z przesyłaniem plików.

Obsługa wielu przesyłanych plików Obsługa wielu przesyłanych

plików w Livewire to ciasto.

Oto ona:

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

Livewire wykryje atrybut "multiple" i obsłuży wszystko za Ciebie.

Po stronie $this->photos serwera właściwość będzie TABLICĄ przesłanych plików.

Możesz je sprawdzać i przechowywać jak każdą inną tablicę:

...
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(','),
    ]);
}
...

Wyświetlanie wskaźników

ładowania Chcesz pokazać użytkownikowi wskaźnik ładowania podczas przesyłania pliku?

Ponownie, zajmij się tym tak, jak zwykle w Livewire zwire:loading:

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

"Przesyłanie ..." Wiadomość będzie teraz wyświetlana przez cały czas przesyłania.

Wyświetlanie wskaźników

postępu Prosty wskaźnik ładowania nie wystarczy?

Livewire wysyła kilka przydatnych zdarzeń JavaScript, które mogą być łatwo podłączone przez coś takiego jak AlpineJS.

Oto prosty wskaźnik ładowania napisany za pomocą Alpine wewnątrz komponentu 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>

Walidacja

plików w czasie rzeczywistym Możesz zweryfikować plik, gdy tylko zostanie wybrany, tak jak sprawdzasz KAŻDĄ wartość po aktualizacji w Livewire.

Podłącz się do "zaktualizowanego" cyklu życia swojej właściwości pliku i gotowe!

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',
    ]);
  }
 
  ...
}

Zauważ, że masz doświadczenie w walidacji w czasie rzeczywistym na froncie, ale kod zaplecza jest tym samym kodem obsługi błędów Laravel, do którego jesteś przyzwyczajony:

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

Direct Upload To S3

Livewire ułatwia użytkownikom przesyłanie plików, które NIGDY nie dotykają Twojego serwera.

Jest to NIEZWYKLE przydatne w przypadku dużych plików lub środowisk bezserwerowych, takich jak Laravel Vapor.

Określ dysk pamięci masowej w aplikacji, który używa s3 sterownika magazynu Laravel, a wszystko będzie działać magicznie:

config / livewire.php

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

Livewire prześle teraz plik tymczasowy bezpośrednio do S3 przy użyciu wstępnie podpisanego adresu URL przesyłania.

Pliki będą przechowywane w katalogu wywoływanym livewire-tmp/ domyślnie.

Aby skonfigurować ten folder do usuwania plików starszych niż 24 godziny, Livewire udostępnia przydatne polecenie rzemieślnicze:Uwaga:

php artisan livewire:configure-s3-upload-cleanup

Nadal możesz traktować $this->photo właściwość jak normalnie bez faktycznego wywoływania S3. Tylko wtedy, gdy uzyskasz dostęp do jego zawartości () lub jego rozmiaru ($this->photo->get();do walidacji: $this->photo->getSize()) zostanie wywołany S3.

Dzięki temu Twoje żądania Livewire są szybkie w przypadku zadań niezwiązanych z przesyłaniem.

Tymczasowe adresy URL podglądu

Czasami możesz chcieć pokazać użytkownikowi podgląd wybranego pliku, zanim naciśnie "Prześlij".

Zwykle jest to trudne, ponieważ tymczasowe pliki do przesłania nie są publicznie dostępne.

Livewire sprawia, że niezwykle łatwo jest generować tymczasowe, bezpieczne, publiczne adresy URL do użytku przeglądarki.

Oto przykład widoku komponentu, który wyświetla podgląd obrazu nowo przesłanego pliku użytkownikowi:Teraz,

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

gdy tylko użytkownik wybierze plik (i po wewnętrznym przetworzeniu nowego przesłania przez Livewire), zobaczy podgląd wyświetlany przed podjęciem decyzji o przesłaniu formularza.

Jeszcze więcej S3 Awesomeness

Jeśli skonfigurowałeś tymczasowy dysk przesyłania Livewire na "s3", wygeneruje ->temporaryUrl() wstępnie podpisany adres URL pliku tymczasowego, który odczytuje bezpośrednio z S3.

Testowanie przesyłania plików Testowanie przesyłania

plików za pomocą Livewire jest niezwykle proste.

Możesz korzystać ze wszystkich funkcji, do których jesteś przyzwyczajony ze standardowymi kontrolerami Laravel.

Oto przykład testu obejmującego przesyłanie plików w komponencie:

/** @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');
}

Integracja z FilepondFilepond

to fantastyczna biblioteka JavaScript, która sprawia, że przeciąganie i upuszczanie oraz inne fantazje związane z przesyłaniem są niezwykle łatwe.

Funkcja przesyłania plików Livewire została stworzona, aby zaspokoić takie integracje.

Jeśli jesteś zainteresowany tym, jak to wygląda, przejdź do Livewire's File Upload Screencasts, aby uzyskać szczegółowy samouczek.

Podpisywanie Whew

, co za funkcja. Proste na pierwszy rzut oka, ale znacznie potężniejsze, jeśli masz głębsze potrzeby.

Moim celem w Livewire jest uczynienie tworzenia stron internetowych w Laravel tak prostym, jak to tylko możliwe (bez "trzymania się za rękę"). Mam nadzieję, że ta funkcja odzwierciedla tę mantrę.

Jeśli jesteś gotowy, aby się zanurzyć, przejdź do dokumentacji lub obejrzyj nową serię screencastów na temat przesyłania plików, aby dowiedzieć się więcej!

Miłego przesyłania!
–Caleb

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

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...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297