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

Як відправити файл з Vue-форми й показати прогрес завантаження?

Поле файлу не працює з v-model - значення <input type="file"> можна лише прочитати:

<script setup lang="ts">
import { ref } from 'vue'
import axios from 'axios'

const file = ref<File | null>(null)
const progress = ref(0)

function onChange(e: Event) {
  file.value = (e.target as HTMLInputElement).files?.[0] ?? null
}

async function upload() {
  if (!file.value) return

  const data = new FormData()
  data.append('avatar', file.value)
  data.append('title', 'Аватар')

  await axios.post('/api/avatar', data, {
    onUploadProgress: (e) => {
      progress.value = e.total ? Math.round((e.loaded / e.total) * 100) : 0
    },
  })
}
</script>

<template>
  <input type="file" accept="image/*" @change="onChange" />
  <progress v-if="progress" :value="progress" max="100" />
  <button @click="upload">Завантажити</button>
</template>

Головні моменти:

  • FormData формує запит multipart/form-data. Заголовок Content-Type не встановлюють вручну - браузер сам додає його з правильним boundary. Ручний Content-Type: multipart/form-data без boundary - сервер не розбере тіло;
  • прогрес завантаження дає XMLHttpRequest (подія upload.onprogress), і саме його використовує axios у браузері. У fetch прогресу відправки немає - лише прогрес отримання відповіді через потоки;
  • PUT/PATCH з файлом у Laravel: PHP не розбирає multipart для цих методів. Відправляйте POST з полем _method=PUT (method spoofing).

Попередній перегляд зображення:

const preview = ref<string | null>(null)
watch(file, (f, _, onCleanup) => {
  if (!f) return
  const url = URL.createObjectURL(f)
  preview.value = url
  onCleanup(() => URL.revokeObjectURL(url))   // звільнити пам'ять
})

Великі файли:

  • скасування - AbortController і signal в опціях axios, щоб користувач міг зупинити завантаження;
  • ліміти: upload_max_filesize і post_max_size у PHP, client_max_body_size у Nginx - помилка 413 чи порожній $request->file() часто означає саме це;
  • пряме завантаження в сховище: сервер видає підписаний URL S3/R2, браузер вантажить файл туди напряму, а застосунку повідомляє лише ключ. PHP не тримає гігабайти в пам'яті й не впирається в тайм-аути;
  • частинами (chunked upload, протокол tus) - для дуже великих файлів і нестабільних з'єднань.

Перевірка на клієнті (accept, розмір файлу) - для зручності; сервер усе одно валідує тип і розмір (mimes, max).

Докладніше в документації: MDN: XMLHttpRequest.upload

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