Як завантажити файл за допомогою API завантаження JS?


170

Я все ще намагаюся обмотати голову навколо цього.

Я можу дозволити користувачеві вибрати файл (або навіть кілька) із введенням файлу:

<form>
  <div>
    <label>Select file to upload</label>
    <input type="file">
  </div>
  <button type="submit">Convert</button>
</form>

І я можу зловити submitподію, використовуючи <fill in your event handler here>. Але як тільки я це зробити, як я можу надіслати файл за допомогою fetch?

fetch('/files', {
  method: 'post',
  // what goes here? What is the "body" for this? content-type header?
}).then(/* whatever */);

1
Офіційний документ працює для мене після спроби деяких відповідей не вдалося: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/… , щось може підтвердити: 1. потрібний обгортковий файл у FromData; 2. не потрібно заявляти Content-Type: multipart/form-dataв заголовку запиту
Spark.Bao

Відповіді:


127

Це базовий приклад з коментарями. uploadФункція то , що ви шукаєте:

// Select your input type file and store it in a variable
const input = document.getElementById('fileinput');

// This will upload the file after having read it
const upload = (file) => {
  fetch('http://www.example.net', { // Your POST endpoint
    method: 'POST',
    headers: {
      // Content-Type may need to be completely **omitted**
      // or you may need something
      "Content-Type": "You will perhaps need to define a content-type here"
    },
    body: file // This is your file object
  }).then(
    response => response.json() // if the response is a JSON object
  ).then(
    success => console.log(success) // Handle the success response object
  ).catch(
    error => console.log(error) // Handle the error response object
  );
};

// Event handler executed when a file is selected
const onSelectFile = () => upload(input.files[0]);

// Add a listener on your input
// It will be triggered when a file will be selected
input.addEventListener('change', onSelectFile, false);

8
Чому цей приклад включає заголовки типу вмісту, але інша відповідь говорить про те, щоб їх не було при надсиланні файлів за допомогою API Fetch? Який це?
jjrabbit

12
НЕ встановлюйте тип вмісту. Я витратив багато часу, намагаючись змусити його працювати, а потім знайшов цю статтю, яка каже, що не встановлювати її. І це працює! muffinman.io/uploading-files-using-fetch-multipart-form-data
Костянтин

як би ти читав цей файл з, скажімо, експрес-бекенда. Оскільки файл не надсилається як дані форми. Натомість він надсилається лише як файл-об’єкт. Чи експрес-завантаження файлів чи мультер аналізує такі корисні навантаження?
sakib11

221

Я зробив це так:

var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'POST',
  body: data
})

16
Вам не потрібно загортати вміст файлу в FormDataоб’єкт, якщо все, що ви завантажуєте, - це файл (чого потрібно оригінальне запитання). fetchприйме input.files[0]вище як його bodyпараметр.
Клаус

17
Якщо у вас є PHP бекенд, який обробляє завантаження файлу, ви захочете загортати файл у FormData, щоб масив $ _FILES був правильно заповнений.
ddelrio1986

2
Я також помітив, що Google Chrome чомусь не відображатиме файл у завантаженні запиту без частини FormData. Схоже, помилка на Мережевій панелі Google Chrome.
ddelrio1986

4
Це дійсно має бути правильною відповіддю. Інший спосіб також працює, але більш
перекручений

що ти маєш на увазі під / аватарами? Ви посилаєтесь на якусь кінцеву точку API заздалегідь?
Kartikeya Mishra

90

Важлива примітка для надсилання файлів за допомогою Fetch API

Потрібно опустити content-typeзаголовок для отримання запиту. Тоді браузер автоматично додасть Content typeзаголовок, включаючи форму межі форми, яка виглядає так

Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryfgtsKTYLsT7PNUVD

Межа форми - це роздільник для даних форми


17
ЦЕ! Дуже важливо! Не використовуйте свій власний тип вмісту з добуванням на багаточастинах. Я поняття не мав, чому мій код не працює.
Ернестас Станкевічус


1
Це золото! Я витратив 1 годину, не розуміючи цього. Дякуємо, що поділилися цією порадою
Ешвін Прабху,

1
Downvote тому, що хоч це корисна інформація, але це не намагається відповісти на питання ОП.
toraritte

3
Це дуже важлива інформація, яка не відображається в документах MDN Fetch .
Plasty Grove

36

Якщо ви хочете кілька файлів, ви можете використовувати це

var input = document.querySelector('input[type="file"]')

var data = new FormData()
for (const file of input.files) {
  data.append('files',file,file.name)
}

fetch('/avatars', {
  method: 'POST',
  body: data
})

@ Saly3301 У мене була така ж проблема, тому що моя функція API намагалася перетворити formData в JSON. (Я лише прокоментував випадковий випадок, що це комусь допомагає)
mp035

19

Щоб подати один файл, ви можете просто використовувати Fileоб’єкт із масиву input's .filesбезпосередньо як значення body:вашого fetch()ініціалізатора:

const myInput = document.getElementById('my-input');

// Later, perhaps in a form 'submit' handler or the input's 'change' handler:
fetch('https://example.com/some_endpoint', {
  method: 'POST',
  body: myInput.files[0],
});

Це працює тому, що Fileуспадковується від Blobта Blobє одним із допустимих BodyInitтипів, визначених у стандарті вилучення.


Це найпростіша відповідь, але як це body: myInput.files[0]призводить до кількості байтів, що зберігаються в пам'яті на стороні клієнта?
бхантол

2
Я б очікував, що за допомогою цього рішення браузер буде достатньо розумним для передачі файлу і не вимагає його читання в пам'яті @bhantol, але я не пішов зі шляху, щоб це дізнатися (або емпірично, або заглиблюючись у специфікація). Якщо ви хочете підтвердити, ви можете спробувати (у кожному з основних браузерів), використовуючи такий підхід, щоб завантажити файл 50 Гб або щось таке, і побачити, чи намагається ваш браузер використовувати занадто багато пам’яті і вбивається.
Марк Амері

Не працювали для мене. express-fileuploadне вдалося проаналізувати потік запитів. Але FormDataпрацює як шарм.
атакоманський

1
@attacomsian На перший погляд, мені express-fileuploadздається, це бібліотека серверів для обробки multipart/form-dataзапитів, що містять файли, так що так, це не сумісно з таким підходом (який просто безпосередньо надсилає файл як орган запиту).
Марк Амері

6

Тут прийнята відповідь трохи датована. Станом на квітень 2020 року рекомендований підхід на веб-сайті MDN передбачає використання, FormDataа також не вимагає встановлення типу вмісту. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Я цитую фрагмент коду для зручності:

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then((response) => response.json())
.then((result) => {
  console.log('Success:', result);
})
.catch((error) => {
  console.error('Error:', error);
});

1
Використання FormDataбуде працювати лише в тому випадку, якщо сервер очікує даних форми. Якщо сервер бажає необробленого файлу як тіла POST, прийнята відповідь є правильною.
Клайд

2

Стрибки з підходу Алекса Монтоя до декількох елементів введення файлів

const inputFiles = document.querySelectorAll('input[type="file"]');
const formData = new FormData();

for (const file of inputFiles) {
    formData.append(file.name, file.files[0]);
}

fetch(url, {
    method: 'POST',
    body: formData })

1

Проблема для мене полягала в тому, що я використовував response.blob () для заповнення даних форми. Мабуть, ви не можете цього зробити принаймні з рідною реакцією, тому я закінчила використання

data.append('fileData', {
  uri : pickerResponse.uri,
  type: pickerResponse.type,
  name: pickerResponse.fileName
 });

Здається, Fetch розпізнає цей формат і надсилає файл, де вказано uri.


0

Ось мій код:

html:

const upload = (file) => {
    console.log(file);

    

    fetch('http://localhost:8080/files/uploadFile', { 
    method: 'POST',
    // headers: {
    //   //"Content-Disposition": "attachment; name='file'; filename='xml2.txt'",
    //   "Content-Type": "multipart/form-data; boundary=BbC04y " //"multipart/mixed;boundary=gc0p4Jq0M2Yt08jU534c0p" //  ή // multipart/form-data 
    // },
    body: file // This is your file object
  }).then(
    response => response.json() // if the response is a JSON object
  ).then(
    success => console.log(success) // Handle the success response object
  ).catch(
    error => console.log(error) // Handle the error response object
  );

  //cvForm.submit();
};

const onSelectFile = () => upload(uploadCvInput.files[0]);

uploadCvInput.addEventListener('change', onSelectFile, false);
<form id="cv_form" style="display: none;"
										enctype="multipart/form-data">
										<input id="uploadCV" type="file" name="file"/>
										<button type="submit" id="upload_btn">upload</button>
</form>
<ul class="dropdown-menu">
<li class="nav-item"><a class="nav-link" href="#" id="upload">UPLOAD CV</a></li>
<li class="nav-item"><a class="nav-link" href="#" id="download">DOWNLOAD CV</a></li>
</ul>


1
З перегляду: Привіт, будь ласка, не відповідайте лише вихідним кодом. Спробуйте надати приємний опис про те, як працює ваше рішення. Дивіться: Як написати гарну відповідь? . Спасибі
sɐunıɔ ןɐ qɐp
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.