Cómo descargar archivos usando axios

Resuelto David Choi asked hace 7 años • 20 respuestas

Estoy usando axios para solicitudes http básicas como GET y POST, y funciona bien. Ahora también necesito poder descargar archivos de Excel. ¿Es esto posible con axios? Si es así, ¿alguien tiene algún código de muestra? Si no, ¿qué más puedo usar en una aplicación React para hacer lo mismo?

David Choi avatar Jan 30 '17 21:01 David Choi
Aceptado
  1. Descargue el archivo con Axios comoresponseType: 'blob'
  2. Cree un enlace de archivo usando el blob en la respuesta de Axios/Server
  3. Cree <a>un elemento HTML con el href vinculado al vínculo del archivo creado en el paso 2 y haga clic en el vínculo
  4. Limpiar el enlace del archivo creado dinámicamente y el elemento HTML
axios({
    url: 'http://api.dev/file-download', //your url
    method: 'GET',
    responseType: 'blob', // important
}).then((response) => {
    // create file link in browser's memory
    const href = URL.createObjectURL(response.data);

    // create "a" HTML element with href to file & click
    const link = document.createElement('a');
    link.href = href;
    link.setAttribute('download', 'file.pdf'); //or any other extension
    document.body.appendChild(link);
    link.click();

    // clean up "a" element & remove ObjectURL
    document.body.removeChild(link);
    URL.revokeObjectURL(href);
});

Consulte las peculiaridades en https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743

Créditos completos a: https://gist.github.com/javilobo8

Más documentación URL.createObjectURLestá disponible en MDN. Es fundamental liberar el objeto URL.revokeObjectURLpara evitar una pérdida de memoria. En la función anterior, como ya descargamos el archivo, podemos revocar el objeto inmediatamente.

Cada vez que llamas a createObjectURL(), se crea una nueva URL de objeto, incluso si ya has creado una para el mismo objeto. Cada uno de estos debe liberarse llamando a URL.revokeObjectURL() cuando ya no los necesite.

Los navegadores publicarán las URL de los objetos automáticamente cuando se descargue el documento; sin embargo, para lograr un rendimiento y un uso de memoria óptimos, si hay momentos seguros en los que puedes descargarlos explícitamente, debes hacerlo.

Vinay avatar Nov 09 '2018 17:11 Vinay

Cuando la respuesta viene con un archivo descargable, los encabezados de respuesta serán algo así como

Content-Disposition: "attachment;filename=report.xls"
Content-Type: "application/octet-stream" // or Content-type: "application/vnd.ms-excel"

Lo que puedes hacer es crear un componente separado, que contendrá un iframe oculto.

  import * as React from 'react';

  var MyIframe = React.createClass({

     render: function() {
         return (
           <div style={{display: 'none'}}>
               <iframe src={this.props.iframeSrc} />
           </div>
         );
     }
  });

Ahora, puede pasar la URL del archivo descargable como accesorio a este componente, de modo que cuando este componente reciba el accesorio, se volverá a representar y se descargará el archivo.

Editar: también puede utilizar el módulo js-file-download . Enlace al repositorio de Github

const FileDownload = require('js-file-download');

Axios({
  url: 'http://localhost/downloadFile',
  method: 'GET',
  responseType: 'blob', // Important
}).then((response) => {
    FileDownload(response.data, 'report.csv');
});
Hardik Modha avatar Jan 30 '2017 16:01 Hardik Modha

Descarga de archivos (usando Axios y Security)

En realidad, esto es aún más complejo cuando desea descargar archivos usando Axios y algunos medios de seguridad. Para evitar que alguien más dedique demasiado tiempo a resolver esto, déjame explicarte esto.

Necesitas hacer 3 cosas:

  1. Configure su servidor para permitir que el navegador vea los encabezados HTTP requeridos
  2. Implemente el servicio del lado del servidor y haga que anuncie el tipo de archivo correcto para el archivo descargado.
  3. Implementación de un controlador Axios para activar un cuadro de diálogo FileDownload dentro del navegador

Estos pasos son en su mayoría factibles, pero se complican considerablemente por la relación del navegador con CORS. Un paso a la vez:

1. Configure su servidor (HTTP)

Cuando se emplea seguridad de transporte, JavaScript que se ejecuta dentro de un navegador puede [por diseño] acceder solo a 6 de los encabezados HTTP realmente enviados por el servidor HTTP. Si deseamos que el servidor sugiera un nombre de archivo para la descarga, debemos informar al navegador que está "OK" que JavaScript tenga acceso a otros encabezados donde se transportaría el nombre de archivo sugerido.

Supongamos, por el bien de la discusión, que queremos que el servidor transmita el nombre de archivo sugerido dentro de un encabezado HTTP llamado X-Suggested-Filename . El servidor HTTP le dice al navegador que está bien exponer este encabezado personalizado recibido a JavaScript/Axios con el siguiente encabezado:

Access-Control-Expose-Headers: X-Suggested-Filename

La forma exacta de configurar su servidor HTTP para establecer este encabezado varía de un producto a otro.

Consulte https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers para obtener una explicación completa y una descripción detallada de estos encabezados estándar.

2. Implementar el servicio del lado del servidor.

La implementación de su servicio del lado del servidor ahora debe realizar 2 cosas:

  1. Cree el documento (binario) y asigne el ContentType correcto a la respuesta
  2. Asigne el encabezado personalizado (X-Suggested-Filename) que contiene el nombre de archivo sugerido para el cliente

Esto se hace de diferentes maneras según la pila de tecnología elegida. Esbozaré un ejemplo utilizando el estándar JavaEE 7 que debería emitir un informe de Excel:

    @GET
    @Path("/report/excel")
    @Produces("application/vnd.ms-excel")
    public Response getAllergyAndPreferencesReport() {

        // Create the document which should be downloaded
        final byte[] theDocumentData = .... 

        // Define a suggested filename
        final String filename = ... 
     
        // Create the JAXRS response
        // Don't forget to include the filename in 2 HTTP headers: 
        //
        // a) The standard 'Content-Disposition' one, and
        // b) The custom 'X-Suggested-Filename'  
        //
        final Response.ResponseBuilder builder = Response.ok(
                theDocumentData, "application/vnd.ms-excel")
                .header("X-Suggested-Filename", fileName);
        builder.header("Content-Disposition", "attachment; filename=" + fileName);

        // All Done.
        return builder.build();
    }

El servicio ahora emite el documento binario (un informe de Excel, en este caso), establece el tipo de contenido correcto y también envía un encabezado HTTP personalizado que contiene el nombre de archivo sugerido para usar al guardar el documento.

3. Implementar un controlador Axios para el documento recibido.

Hay algunos errores aquí, así que asegurémonos de que todos los detalles estén configurados correctamente:

  1. El servicio responde a @GET (es decir, HTTP GET), por lo que la llamada de Axios debe ser 'axios.get(...)'.
  2. El documento se transmite como un flujo de bytes, por lo que debe indicarle a Axios que trate la respuesta como un Blob HTML5. (Es decir, tipo de respuesta: 'blob' ).
  3. En este caso, la biblioteca JavaScript de ahorro de archivos se utiliza para abrir el cuadro de diálogo del navegador. Sin embargo, podrías elegir otro.

La implementación básica de Axios sería algo así como:

     // Fetch the dynamically generated excel document from the server.
     axios.get(resource, {responseType: 'blob'}).then((response) => {

        // Log somewhat to show that the browser actually exposes the custom HTTP header
        const fileNameHeader = "x-suggested-filename";
        const suggestedFileName = response.headers[fileNameHeader];
        const effectiveFileName = (suggestedFileName === undefined
                    ? "allergierOchPreferenser.xls"
                    : suggestedFileName);
        console.log(`Received header [${fileNameHeader}]: ${suggestedFileName}, effective fileName: ${effectiveFileName}`);

        // Let the user save the file.
        FileSaver.saveAs(response.data, effectiveFileName);

        }).catch((response) => {
            console.error("Could not Download the Excel report from the backend.", response);
        });
Lennart Jörelid avatar Aug 26 '2017 13:08 Lennart Jörelid

Solución Axios.post con IE y otros navegadores

He encontrado algunas soluciones increíbles aquí. Pero frecuentemente no toman en cuenta los problemas con el navegador IE. Quizás le ahorre algo de tiempo a alguien más.

axios.post("/yourUrl",
    data,
    { responseType: 'blob' }
).then(function (response) {
    let fileName = response.headers["content-disposition"].split("filename=")[1];
    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE variant
        window.navigator.msSaveOrOpenBlob(new Blob([response.data],
                { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
            ),
            fileName
        );
    } else {
        const url = window.URL.createObjectURL(new Blob([response.data],
            { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }));
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download',
            response.headers["content-disposition"].split("filename=")[1]);
        document.body.appendChild(link);
        link.click();
    }
    }
);

El ejemplo anterior es para archivos de Excel, pero con pequeños cambios se puede aplicar a cualquier formato.

Y en el servidor hice esto para enviar un archivo de Excel.

response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")
Alex avatar May 24 '2019 11:05 Alex

La función para realizar la llamada API con axios:

function getFileToDownload (apiUrl) {
   return axios.get(apiUrl, {
     responseType: 'arraybuffer',
     headers: {
       'Content-Type': 'application/json'
     }
   })
}

Llame a la función y luego descargue el archivo de Excel que obtiene:

getFileToDownload('putApiUrlHere')
  .then (response => {
      const type = response.headers['content-type']
      const blob = new Blob([response.data], { type: type, encoding: 'UTF-8' })
      const link = document.createElement('a')
      link.href = window.URL.createObjectURL(blob)
      link.download = 'file.xlsx'
      link.click()
  })
Roland avatar Jul 23 '2019 12:07 Roland