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 */);
Deitch
sumber
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);
Damien
sumber
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
})
Integ
sumber
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

madhu131313
sumber
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
})
Alex Montoya
sumber
@ 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.

Mark Amery
sumber
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);
});
Raiyan
sumber
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 })
Jerald Macachor
sumber
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.

NickJ
sumber
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>

Andreas Patsimas
sumber
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