Как загрузить файл с помощью API выборки JS?


171

Я все еще пытаюсь обернуть голову вокруг этого.

Я могу предложить пользователю выбрать файл (или даже несколько) с помощью ввода файла:

<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
указывать

Ответы:


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
Почему в этом примере содержатся заголовки Content-Type, а в другом ответе говорится, что они должны опускаться при отправке файлов с помощью Fetch API? Который из них?
jjrabbit

12
НЕ устанавливайте Content-Type. Я потратил много времени, пытаясь заставить его работать, а затем обнаружил, что в этой статье сказано, что это не так. И это работает! muffinman.io/uploading-files-using-fetch-multipart-form-data
Константин

Как бы вы прочитали этот файл, скажем, Express Backend. Поскольку файл не отправляется как данные формы. Вместо этого он отправляется как просто объект файла. Экспресс-fileupload или multer анализируют такие полезные нагрузки?
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

3
Я также заметил, что Google Chrome по какой-то причине не отображает файл в полезной нагрузке запроса без части FormData. Похоже, ошибка в панели сети Google Chrome.
ddelrio1986

4
Это действительно должен быть правильный ответ. Другой способ также работает, но более запутан
jnmandal

что вы подразумеваете под / аватарами? Вы имеете в виду какую-то конечную точку API бэкенда?
Картикея Мишра

90

Важное примечание для отправки файлов с помощью Fetch API

Нужно опустить content-typeзаголовок для запроса Fetch. Затем браузер автоматически добавит Content typeзаголовок, включая границу формы, которая выглядит как

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

Граница формы - это разделитель для данных формы


17
ЭТОТ! Очень важно! Не используйте свой собственный тип контента с fetch на multipart. Я понятия не имел, почему мой код не работает.
Эрнестас Станкявичюс


1
Это золото! Я потратил 1 час, не понимая этого. Спасибо за то, что поделились этим советом
Эшвин Прабху

1
Даунвот, потому что это хоть и полезная информация, но это не попытка ответить на вопрос ОП.
Тораритте

3
Это очень важная информация, которая не отражена в документах MDN Fetch .
Пластическая роща

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.