我使用axios的基本http请求,如GET和POST,它工作得很好。现在我需要能够下载Excel文件。这在axios中可行吗?如果是,谁有一些示例代码?如果不是,我还可以在React应用程序中使用什么来做同样的事情?


当前回答

有几个关键点大部分答案都被忽略了。

我将在这里进行更深入的解释。

TLDR;

如果您正在创建一个标记链接并通过浏览器请求启动下载,那么

总是调用window.URL.revokeObjectURL(url);。否则就会有 不必要的内存尖峰。 不需要使用document.body. appendchild (link);将创建的链接附加到文档主体,从而避免以后不必要地删除子链接。


有关组件代码和更深入的分析,请进一步阅读

首先要弄清楚试图从中下载数据的API端点是公共的还是私有的。你能控制服务器吗?


如果服务器响应

Content-Disposition: attachment; filename=dummy.pdf
Content-Type: application/pdf

浏览器总是会尝试下载名称为'dummy.pdf'的文件


如果服务器响应

Content-Disposition: inline; filename=dummy.pdf
Content-Type: application/pdf

浏览器将首先尝试打开一个本地文件阅读器,如果名称为'dummy.pdf',否则它将开始下载文件。


如果服务器没有响应以上两个头

如果没有设置下载属性,浏览器(至少chrome)将尝试打开该文件。如果设置,它将下载文件。在url不是blob的情况下,文件名将是最后一个路径参数的值。


除此之外,请记住使用transfer - encoding:从服务器传输大量数据。这将确保客户端知道在缺少Content-Length报头的情况下何时停止读取当前请求

私人档案

import { useState, useEffect } from "react";
import axios from "axios";

export default function DownloadPrivateFile(props) {
  const [download, setDownload] = useState(false);

  useEffect(() => {
    async function downloadApi() {
      try {
        // It doesn't matter whether this api responds with the Content-Disposition header or not
        const response = await axios.get(
          "http://localhost:9000/api/v1/service/email/attachment/1mbdoc.docx",
          {
            responseType: "blob", // this is important!
            headers: { Authorization: "sometoken" },
          }
        );
        const url = window.URL.createObjectURL(new Blob([response.data])); // you can mention a type if you wish
        const link = document.createElement("a");
        link.href = url;
        link.setAttribute("download", "dummy.docx"); //this is the name with which the file will be downloaded
        link.click();
        // no need to append link as child to body.
        setTimeout(() => window.URL.revokeObjectURL(url), 0); // this is important too, otherwise we will be unnecessarily spiking memory!
        setDownload(false);
      } catch (e) {} //error handling }
    }

    if (download) {
      downloadApi();
    }
  }, [download]);

  return <button onClick={() => setDownload(true)}>Download Private</button>;
}


公共档案

import { useState, useEffect } from "react";
export default function DownloadPublicFile(props) {
  const [download, setDownload] = useState(false);

  useEffect(() => {
    if (download) {
      const link = document.createElement("a");
      link.href =
        "http://localhost:9000/api/v1/service/email/attachment/dummy.pdf";
      link.setAttribute("download", "dummy.pdf");
      link.click();
      setDownload(false);
    }
  }, [download]);

  return <button onClick={() => setDownload(true)}>Download Public</button>;
}

很高兴知道:

Always control file downloads from server. Axios in the browser uses XHR under the hood, in which streaming of responses is not supported. Use onDownloadProgress method from Axios to implement progress bar. Chunked responses from server do not ( cannot ) indicate Content-Length. Hence you need some way of knowing the response size if you are using them while building a progress bar. <a> tag links can only make GET HTTP requests without any ability to send headers or cookies to the server (ideal for downloading from public endpoints) Brower request is slightly different from XHR request made in code.

参考:AJAX请求和常规浏览器请求之间的区别

其他回答

对于axios POST请求,请求应该是这样的: 这里的关键是responseType和header字段必须在Post的第3个参数中。第二个参数是应用程序参数。

export const requestDownloadReport = (requestParams) => async dispatch => { 
  let response = null;
  try {
    response = await frontEndApi.post('createPdf', {
      requestParams: requestParams,
    },
    {
      responseType: 'arraybuffer', // important...because we need to convert it to a blob. If we don't specify this, response.data will be the raw data. It cannot be converted to blob directly.
      headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/pdf'
      }
  });          
  }
  catch(err) {
    console.log('[requestDownloadReport][ERROR]', err);
    return err
  }

  return response;
}

这对我很管用。我在reactJS中实现了这个解决方案

const requestOptions = {`enter code here`
method: 'GET',
headers: { 'Content-Type': 'application/json' }
};

fetch(`${url}`, requestOptions)
.then((res) => {
    return res.blob();
})
.then((blob) => {
    const href = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = href;
    link.setAttribute('download', 'config.json'); //or any other extension
    document.body.appendChild(link);
    link.click();
})
.catch((err) => {
    return Promise.reject({ Error: 'Something Went Wrong', err });
})
        axios.get(
            '/app/export'
        ).then(response => {    
            const url = window.URL.createObjectURL(new Blob([response]));
            const link = document.createElement('a');
            link.href = url;
            const fileName = `${+ new Date()}.csv`// whatever your file name .
            link.setAttribute('download', fileName);
            document.body.appendChild(link);
            link.click();
            link.remove();// you need to remove that elelment which is created before.
})

下载带有Axios的文件作为responseType: 'blob' 使用Axios/Server响应中的blob创建文件链接 创建<a> HTML元素,使用href链接到步骤2中创建的文件链接,并单击该链接 清理动态创建的文件链接和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);
});

在https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743上查看这些怪癖

全部演职员表发送至:https://gist.github.com/javilobo8

URL的更多文档。createObjectURL在MDN上可用。释放带有URL的对象非常关键。revokeObjectURL防止内存泄漏。在上面的函数中,因为我们已经下载了文件,所以可以立即撤销对象。

每次调用createObjectURL()时,都会创建一个新的对象URL,即使您已经为同一个对象创建了一个URL。当你不再需要它们时,必须调用URL.revokeObjectURL()来释放它们。

当文档被卸载时,浏览器会自动释放对象url;但是,为了优化性能和内存使用,如果存在可以显式卸载它们的安全时间,则应该这样做。

当响应带有可下载文件时,响应头将类似于

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

您可以创建一个单独的组件,该组件将包含一个隐藏的iframe。

  import * as React from 'react';

  var MyIframe = React.createClass({

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

现在,你可以将可下载文件的url作为prop传递给这个组件,所以当这个组件接收到prop时,它将重新渲染,文件将被下载。

编辑:你也可以使用js-file-download模块。链接到Github回购

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

Axios({
  url: 'http://localhost/downloadFile',
  method: 'GET',
  responseType: 'blob', // Important
}).then((response) => {
    FileDownload(response.data, 'report.csv');
});