Come faccio a caricare un file con l'API di recupero JS?


170

Sto ancora cercando di avvolgerci la testa.

Posso fare in modo che l'utente selezioni il file (o anche più) con l'input del file:

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

E posso seguire l' submitevento usando <fill in your event handler here>. Ma una volta fatto, come posso inviare il file usando fetch?

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

1
il documento ufficiale funziona per me dopo aver provato alcune risposte non riuscite: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/… , qualcosa può confermare: 1. bisogno di avvolgere il file in FromData; 2. Non è necessario dichiarare Content-Type: multipart/form-datanell'intestazione della richiesta
Spark.Bao,

Risposte:


127

Questo è un esempio di base con commenti. La uploadfunzione è ciò che stai cercando:

// 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
Perché questo esempio include le intestazioni Content-Type ma un'altra risposta dice di ometterle quando si inviano file con API Fetch? Qual é?
jjrabbit,

12
NON impostare il tipo di contenuto. Ho trascorso molto tempo a cercare di farlo funzionare e poi ho trovato questo articolo dicendo di non impostarlo. E funziona! muffinman.io/uploading-files-using-fetch-multipart-form-data
Kostiantyn

come leggeresti questo file diciamo Express backend. Poiché il file non viene inviato come dati del modulo. Viene invece inviato come solo oggetto file. Express-FileUpload o Multer analizza tali payload?
sakib11,

221

L'ho fatto così:

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
Non è necessario racchiudere il contenuto del file in un FormDataoggetto se tutto ciò che stai caricando è il file (che è quello che vuole la domanda originale). fetchaccetterà input.files[0]sopra come bodyparametro.
Klaus,

17
Se si dispone di un back-end PHP che gestisce il caricamento del file, si desidera avvolgere il file in un FormData in modo che l'array $ _FILES sia correttamente popolato.
ddelrio1986,

3
Ho anche notato che Google Chrome non avrebbe mostrato il file nel payload della richiesta senza la parte FormData per qualche motivo. Sembra un bug nel pannello Rete di Google Chrome.
ddelrio1986,

4
Questa dovrebbe davvero essere la risposta corretta. Anche l'altro modo funziona, ma è più contorto
jnmandal

cosa intendi con / avatar? Ti riferisci ad un endpoint API back-end?
Kartikeya Mishra,

90

Una nota importante per l'invio di file con API Fetch

È necessario omettere l' content-typeintestazione per la richiesta di recupero. Quindi il browser aggiungerà automaticamente l' Content typeintestazione incluso il confine del modulo che appare

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

Il limite del modulo è il delimitatore per i dati del modulo


17
QUESTO! Molto importante! Non utilizzare il proprio tipo di contenuto con fetch su multipart. Non avevo idea del perché il mio codice non funzionasse.
Ernestas Stankevičius,


1
Questo è oro! Ho perso 1 ora non capendolo. Grazie per aver condiviso questo suggerimento
Ashwin Prabhu,

1
Downvote perché anche se si tratta di informazioni utili, ma questo non tenta di rispondere alla domanda di OP.
Toraritte,

3
Si tratta di informazioni molto importanti che non vengono acquisite nei documenti MDN Fetch .
Plasty Grove,

36

Se vuoi più file, puoi usarlo

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 Ho avuto lo stesso problema, perché la mia funzione API stava cercando di convertire il formData in JSON. (Ho solo commentato la
minima

19

Per inviare un singolo file, si può semplicemente utilizzare l' Fileoggetto dalla input's .filesmatrice direttamente come il valore della body:nella vostra fetch()inizializzatore:

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

Questo funziona perché Fileeredita da Blobed Blobè uno dei BodyInittipi consentiti definiti nello standard di recupero.


Questa è la risposta più semplice, ma come body: myInput.files[0]causa la quantità di byte conservati in memoria sul lato client?
Bhantol,

2
Mi aspetto che con questa soluzione il browser sia abbastanza ragionevole da trasmettere in streaming il file e non richiederne la lettura in memoria, @bhantol, ma non ho fatto di tutto per scoprirlo (empiricamente o approfondendo le specifiche). Se volessi confermare, potresti provare (in ciascuno dei principali browser) usando questo approccio per caricare un file da 50 GB o qualcosa del genere e vedere se il tuo browser tenta di usare troppa memoria e viene ucciso.
Mark Amery,

Non ha funzionato per me. express-fileuploadimpossibile analizzare il flusso di richieste. Ma FormDatafunziona come un fascino.
Attacomsian,

1
@attacomsian A prima vista, mi sembra express-fileuploaduna libreria sul lato server per la gestione delle multipart/form-datarichieste che contengono file, quindi sì, non è compatibile con questo approccio (che invia direttamente il file come corpo della richiesta).
Mark Amery,

6

La risposta accettata qui è un po 'datata. A partire da aprile 2020, un approccio raccomandato visto sul sito Web MDN suggerisce l'utilizzo FormDatae inoltre non richiede di impostare il tipo di contenuto. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Sto citando lo snippet di codice per comodità:

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
L'uso FormDatafunzionerà solo se il server prevede i dati del modulo. Se il server desidera un file non elaborato come corpo del POST, la risposta accettata è corretta.
Clyde

2

Saltare fuori dall'approccio di Alex Montoya per più elementi di input di 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

Il problema per me era che stavo usando response.blob () per popolare i dati del modulo. Apparentemente non puoi farlo almeno con reagire nativo, quindi ho finito per usare

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

Fetch sembra riconoscere quel formato e inviare il file a cui punta l'uri.


0

Ecco il mio codice:

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
Dalla recensione: Ciao, per favore non rispondere solo con il codice sorgente. Prova a fornire una bella descrizione del funzionamento della tua soluzione. Vedi: Come posso scrivere una buona risposta? . Grazie
sɐunıɔ ןɐ qɐp
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.