Як завантажити файл за допомогою 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.