¿Cómo subo un archivo con la API de búsqueda de JS?

170

Todavía estoy tratando de entenderlo.

Puedo hacer que el usuario seleccione el archivo (o incluso varios) con la entrada del archivo:

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

Y puedo ver el submitevento usando <fill in your event handler here>. Pero una vez que lo hago, ¿cómo envío el archivo usando fetch?

fetch('/files', {
  method: 'post',
  // what goes here? What is the "body" for this? content-type header?
}).then(/* whatever */);
deitch
fuente
1
el documento oficial funciona para mí después de intentar algunas respuestas fallidas: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/… , algo puede confirmar: 1. necesita un archivo de ajuste en FromData; 2. no necesita declarar Content-Type: multipart/form-dataen el encabezado de la solicitud
Spark.Bao

Respuestas:

127

Este es un ejemplo básico con comentarios. La uploadfunción es lo que estás buscando:

// 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
fuente
8
¿Por qué este ejemplo incluye encabezados Content-Type pero otra respuesta dice omitirlos al enviar archivos con Fetch API? ¿Cuál es?
jjrabbit
12
NO establezca el tipo de contenido. Pasé mucho tiempo tratando de hacerlo funcionar y luego encontré este artículo que decía que no lo configurara. ¡Y funciona! muffinman.io/uploading-files-using-fetch-multipart-form-data
Kostiantyn
¿cómo leerías este archivo? Digamos el backend Express. Como el archivo no se envía como datos de formulario. En su lugar, se envía solo como el objeto de archivo. ¿Express-fileupload o multer analiza tales cargas útiles?
sakib11
221

Lo he hecho así:

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
fuente
16
No necesita envolver el contenido del archivo en un FormDataobjeto si todo lo que está cargando es el archivo (que es lo que quiere la pregunta original). fetchaceptará input.files[0]arriba como su bodyparámetro.
Klaus
17
Si tiene un backend PHP que maneja la carga del archivo, querrá envolver el archivo en un FormData para que la matriz $ _FILES se complete correctamente.
ddelrio1986
3
También noté que Google Chrome no mostraría el archivo en la carga útil de la solicitud sin la parte FormData por alguna razón. Parece un error en el panel de red de Google Chrome.
ddelrio1986
44
Esta realmente debería ser la respuesta correcta. La otra forma también funciona pero es más complicada
jnmandal
¿Qué quieres decir con / avatares? ¿Te refieres a algún punto final API de back-end?
Kartikeya Mishra
90

Una nota importante para enviar archivos con Fetch API

Hay que omitir el content-typeencabezado de la solicitud Fetch. Luego, el navegador agregará automáticamente el Content typeencabezado, incluido el límite del formulario, que se ve como

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

El límite del formulario es el delimitador de los datos del formulario.

madhu131313
fuente
17
¡ESTA! ¡Muy importante! No use su propio tipo de contenido con fetch en multiparte. No tenía idea de por qué mi código no funciona.
Ernestas Stankevičius
1
Esto es oro! Perdí 1 hora sin entender esto. Gracias por compartir este consejo
Ashwin Prabhu
1
Voto negativo porque aunque es información útil, pero esto no intenta responder a la pregunta de OP.
toraritte
3
Esta es información muy importante que no se captura en los documentos MDN Fetch .
Plasty Grove el
36

Si desea varios archivos, puede usar esto

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
fuente
@ Saly3301 Tuve el mismo problema, fue porque mi función API estaba tratando de convertir el formData a JSON. (Solo comenté la posibilidad de que ayude a alguien)
mp035
19

Para enviar un solo archivo, simplemente puede usar el Fileobjeto de la matriz de input's .filesdirectamente como el valor de body:en su fetch()inicializador:

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

Esto funciona porque Filehereda de Bloby Blobes uno de los BodyInittipos permitidos definidos en el Estándar de recuperación.

Mark Amery
fuente
Esta es la respuesta más simple, pero ¿cómo body: myInput.files[0]causa la cantidad de bytes en memoria en el lado del cliente?
Bhantol
2
Yo espero que con esta solución el navegador sería lo suficientemente sensible para transmitir los archivos y no requiere que se pueda leer en la memoria, @bhantol, pero no he salido de mi manera de descubrir (ya sea empírica o profundizando en La especificación). Si desea confirmar, puede intentar (en cada uno de los principales navegadores) usar este enfoque para cargar un archivo de 50 GB o algo así, y ver si su navegador intenta usar demasiada memoria y se mata.
Mark Amery
No funciono para mi. express-fileuploadno se pudo analizar la secuencia de solicitud. Pero FormDatafunciona como un encanto.
Attacomsian
1
@attacomsian De un vistazo, me parece que express-fileuploades una biblioteca del lado del servidor para manejar multipart/form-datasolicitudes que contienen archivos, así que sí, no es compatible con este enfoque (que simplemente envía el archivo directamente como el cuerpo de la solicitud).
Mark Amery
6

La respuesta aceptada aquí es un poco anticuada. A partir de abril de 2020, un enfoque recomendado visto en el sitio web de MDN sugiere usar FormDatay tampoco pide establecer el tipo de contenido. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Cito el fragmento de código por conveniencia:

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
fuente
1
El uso FormDatasolo funcionará si el servidor espera datos de formulario. Si el servidor quiere un archivo sin procesar como el cuerpo de la POST, la respuesta aceptada es correcta.
Clyde
2

Saltando del enfoque de Alex Montoya para múltiples elementos de entrada de archivos

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
fuente
1

El problema para mí fue que estaba usando un response.blob () para completar los datos del formulario. Aparentemente no puedes hacer eso al menos con react native, así que terminé usando

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

Fetch parece reconocer ese formato y enviar el archivo a donde apunta la uri.

NickJ
fuente
0

Aquí está mi código:

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
fuente
1
De la opinión: Hola, por favor no responda solo con el código fuente. Intente proporcionar una buena descripción sobre cómo funciona su solución. Ver: ¿Cómo escribo una buena respuesta? . Gracias
sɐunıɔ ןɐ qɐp