Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як правильно завантажувати файли з React-форми: попередній перегляд, прогрес і пам'ять?

Файлове поле завжди неконтрольоване: задати йому значення з коду не можна, тож value не передають. Файли читають з події чи з FormData.

function AvatarForm() {
  const [preview, setPreview] = useState(null);

  useEffect(() => () => preview && URL.revokeObjectURL(preview), [preview]);

  function onChange(e) {
    const file = e.target.files?.[0];
    if (!file) return;
    if (file.size > 2 * 1024 * 1024) {
      alert('Файл більший за 2 МБ');
      e.target.value = '';                    // скинути вибір
      return;
    }
    setPreview(URL.createObjectURL(file));
  }

  async function upload(formData) {
    await fetch('/api/avatar', { method: 'POST', body: formData, headers: { Accept: 'application/json' } });
  }

  return (
    <form action={upload}>
      <input type="file" name="avatar" accept="image/png,image/jpeg" onChange={onChange} />
      {preview && <img src={preview} alt="Попередній перегляд" width={120} />}
      <button>Завантажити</button>
    </form>
  );
}

Що тут важливо:

  • URL.createObjectURL створює посилання на файл у пам'яті браузера. Його треба звільняти (revokeObjectURL) при заміні файлу й знищенні компонента, інакше пам'ять не звільняється до закриття вкладки;
  • Content-Type не задавати вручну для FormData - браузер сам поставить multipart/form-data з межею (boundary);
  • accept і перевірка розміру - лише зручність; сервер перевіряє тип за вмістом (image, mimes) і розмір заново;
  • multiple - e.target.files дає список; у FormData - formData.getAll('photos').

Прогрес завантаження. fetch не повідомляє про прогрес відправки тіла. Для смуги прогресу - XMLHttpRequest (xhr.upload.onprogress) чи бібліотека, що його використовує.

Великі файли (відео, архіви):

  • завантаження частинами з продовженням після обриву;
  • пряме завантаження в S3 за підписаним URL, отриманим від Laravel (Storage::temporaryUploadUrl()): файл іде з браузера в сховище, минаючи сервер застосунку;
  • обмеження сервера (upload_max_filesize, client_max_body_size у Nginx) мають відповідати очікуваним розмірам.

PUT/PATCH з файлами в Laravel: PHP історично розбирає multipart лише для POST - тому відправляють POST з полем _method=PUT.

Доступність і UX: кнопка вибору файлу має мітку, а для перетягування (drag and drop) лишається звичайне поле як запасний варіант.

Докладніше в документації: input: читання значень при відправці

Схожі питання