Bagaimana cara mengunggah file dengan API ambil JS?


170

Saya masih mencoba untuk membungkus kepala saya di sekitarnya.

Saya dapat meminta pengguna memilih file (atau bahkan banyak) dengan input file:

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

Dan saya bisa menangkap submitacara menggunakan <fill in your event handler here>. Tapi begitu saya lakukan, bagaimana cara mengirim file menggunakan fetch?

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

1
dokumen resmi berfungsi untuk saya setelah mencoba beberapa jawaban gagal: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/… , sesuatu dapat mengonfirmasi: 1. perlu membungkus file dalam FromData; 2. tidak perlu menyatakan Content-Type: multipart/form-datadalam header permintaan
— Spark.Bao

Jawaban:


127

Ini adalah contoh dasar dengan komentar. The uploadFungsi adalah apa yang Anda cari:

// 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
Mengapa contoh ini menyertakan header Jenis Konten tetapi jawaban lain mengatakan untuk menghilangkannya saat mengirim file dengan Ambil API? Yang mana?
— jjrabbit

12
JANGAN atur Tipe-Konten. Saya menghabiskan banyak waktu mencoba membuatnya bekerja dan kemudian menemukan artikel ini mengatakan untuk tidak mengaturnya. Dan itu berhasil! muffinman.io/uploading-files-using-fetch-multipart-form-data
— Kostiantyn

bagaimana Anda membaca file ini dari katakanlah Express backend. Karena file tidak dikirim sebagai data formulir. Ini bukan dikirim hanya sebagai objek file. Apakah express-fileupload atau multer mem-parsing payloads seperti itu?
— sakib11

221

Saya sudah melakukannya seperti ini:

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
Anda tidak perlu membungkus konten file dalam suatu FormDataobjek jika semua yang Anda unggah adalah file (yang diinginkan pertanyaan aslinya). fetchakan menerima di input.files[0]atas sebagai bodyparameternya.
— Klaus

17
Jika Anda memiliki PHP backend yang menangani unggahan file, Anda ingin membungkus file dalam FormData sehingga array $ _FILES terisi dengan benar.
— ddelrio1986

3
Saya juga memperhatikan bahwa Google Chrome tidak akan menampilkan file di payload permintaan tanpa bagian FormData karena beberapa alasan. Sepertinya ada bug di panel Jaringan Google Chrome.
— ddelrio1986

4
Ini benar-benar jawaban yang benar. Cara lain juga berfungsi tetapi lebih berbelit
— jnmandal

apa yang kamu maksud dengan / avatar? Apakah Anda merujuk ke beberapa titik akhir API backend?
— Kartikeya Mishra

90

Catatan penting untuk mengirim File dengan Ambil API

Orang harus menghilangkan content-typetajuk untuk permintaan Ambil. Kemudian browser akan secara otomatis menambahkan Content typeheader termasuk Form Boundary yang terlihat seperti

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

Batas bentuk adalah pembatas untuk formulir data


17
INI! Sangat penting! Jangan gunakan tipe konten Anda sendiri dengan mengambil multipart. Saya tidak tahu mengapa kode saya tidak berfungsi.
— Ernestas Stankevičius


1
Ini emas! Saya sia-sia 1 jam tidak mengerti ini. Terima kasih telah berbagi tip ini
— Ashwin Prabhu

1
Turun karena karena ini adalah info yang berguna, tetapi ini tidak berusaha menjawab pertanyaan OP.
— Toraritte

3
Ini adalah informasi yang sangat penting yang tidak ditangkap dalam MDN Fetch docs .
— Plasty Grove

36

Jika Anda ingin banyak file, Anda dapat menggunakan ini

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 Saya punya masalah yang sama, itu karena fungsi API saya mencoba untuk mengubah formData ke JSON. (Saya hanya berkomentar jika itu membantu seseorang)
— mp035

19

Untuk mengirimkan file tunggal, Anda hanya dapat menggunakan Fileobjek dari input's .filesarray yang langsung sebagai nilai body:di Anda fetch()initializer:

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],
});

Ini berfungsi karena Filemewarisi dari Blob, dan Blobmerupakan salah satu BodyInitjenis yang diizinkan yang didefinisikan dalam Standar Ambil.


Ini adalah jawaban yang paling sederhana tetapi bagaimana body: myInput.files[0]menyebabkan jumlah byte yang disimpan dalam memori di sisi klien?
— bhantol

2
Saya berharap bahwa dengan solusi ini browser akan cukup masuk akal untuk melakukan streaming file dan tidak mengharuskannya untuk dibaca ke dalam memori, @bhantol, tapi saya belum keluar dari cara saya untuk mencari tahu (baik secara empiris atau dengan menggali ke dalam spec). Jika Anda ingin mengonfirmasi, Anda dapat mencoba (di masing-masing browser utama) menggunakan pendekatan ini untuk mengunggah file 50GB atau sesuatu, dan melihat apakah browser Anda mencoba menggunakan terlalu banyak memori dan terbunuh.
— Mark Amery

Tidak bekerja untuk saya. express-fileuploadgagal mem-parsing aliran permintaan. Tetapi FormDatabekerja seperti pesona.
— attacomsian

1
@attacomsian Sekilas, bagi saya sepertinya express-fileuploadadalah pustaka serveride untuk menangani multipart/form-datapermintaan yang berisi file, jadi ya, itu tidak kompatibel dengan pendekatan ini (yang langsung mengirim file sebagai badan permintaan).
— Mark Amery

6

Jawaban yang diterima di sini sedikit tertanggal. Pada April 2020, pendekatan yang disarankan terlihat di situs web MDN menyarankan penggunaan FormDatadan juga tidak meminta untuk mengatur jenis konten. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Saya mengutip cuplikan kode untuk kenyamanan:

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
Menggunakan FormDatahanya akan berfungsi jika server mengharapkan data formulir. Jika server menginginkan file mentah sebagai isi POST, jawaban yang diterima benar.
— Clyde

2

Melompat dari pendekatan Alex Montoya untuk beberapa elemen input file

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

Masalahnya bagi saya adalah bahwa saya menggunakan response.blob () untuk mengisi formulir data. Tampaknya Anda tidak bisa melakukan itu setidaknya dengan reaksi asli jadi saya akhirnya menggunakan

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

Ambil tampaknya mengenali format itu dan mengirim file ke mana uri menunjuk.


0

Ini kode saya:

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
Dari Ulasan: Hai, tolong jangan jawab hanya dengan kode sumber. Cobalah untuk memberikan deskripsi yang bagus tentang cara kerja solusi Anda. Lihat: Bagaimana saya menulis jawaban yang baik? . Terima kasih
— sɐunıɔ ןɐ qɐp
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.