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


当前回答

文件下载自定义头请求。在这个例子中,它展示了如何用承载令牌发送文件下载请求。适合授权下载内容。

下载(urlHere) { axios。get (urlHere, { 标题:{ “Access-Control-Allow-Origin”:“*”, 授权:'持有人${sessionStorage.getItem("auth-token")} ', } }).then((response) => { const temp = window.URL。createObjectURL(新团([response.data])); const link = document.createElement('a'); 链接。Href = temp; 链接。setAttribute(“下载”,“file.csv”);//或任何其他扩展 document.body.appendChild(链接); link.click (); }); }

其他回答

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

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

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请求和常规浏览器请求之间的区别

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

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');
});

这个函数将帮助您下载一个现成的xlsx, csv等文件下载。我只是从后端发送一个就绪的xlsx静态文件,它在反应。

const downloadFabricFormat = async () => {
    try{
      await axios({
        url: '/api/fabric/fabric_excel_format/',
        method: 'GET',
        responseType: 'blob',
      }).then((response) => {
         const url = window.URL.createObjectURL(new Blob([response.data]));
         const link = document.createElement('a');
         link.href = url;
         link.setAttribute('download', 'Fabric Excel Format.xlsx');
         document.body.appendChild(link);
         link.click();
      });
    } catch(error){
      console.log(error)
    }
  };

使用URL.CreateObject()的答案对我来说工作得很好。 我仍然想指出使用HTTP报头的选项。

使用HttpHeaders有以下优点:

广泛的浏览器支持 不需要在浏览器的内存中创建一个blob对象 在显示给用户反馈之前,不需要等待服务器的完整响应吗 无尺寸限制

使用HttpHeaders需要你访问下载文件的后端服务器(这似乎是OP的Excel文件的情况)

HttpHeaders解决方案:

前端:

//... 
// the download link
<a href="download/destination?parameter1=foo&param2=bar"> 
click me to download!
</a>

后端

(本例中是c#,但也可以是任何语言。根据需要进行调整)

...
var fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Read);
Response.Headers["Content-Disposition"] = "attachment; filename=someName.txt";
return File(fs, "application/octet-stream");
...

此解决方案假定您可以控制响应的后端服务器。

https://github.com/eligrey/FileSaver.js/wiki/Saving-a-remote-file#using-http-header

Axios。后解决方案与IE和其他浏览器

我在这里找到了一些不可思议的解决方案。但他们通常不会考虑IE浏览器的问题。也许这样可以为别人节省一些时间。

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();
    }
    }
);

上面的例子适用于excel文件,但是稍加改变就可以应用于任何格式。

在服务器上,我这样做是为了发送一个excel文件。

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

response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")