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


当前回答

使用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的文件作为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');
});

使用axios进行API调用的函数:

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

调用函数,然后下载excel文件:

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

这是为用户触发下载的非常简单的javascript代码:

window.open("<insert URL here>")

这个操作不需要axios;它应该是标准的,只是让浏览器做它的事情。

注意:如果您需要授权下载,那么这可能无法工作。我很确定你可以使用cookie来授权这样的请求,前提是它在同一个域中,但不管怎样,在这种情况下,这可能不会立即起作用。


至于是否可能……没有内置的文件下载机制,没有。

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

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

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