如何使用axios下载文件


124

我将axios用于GET和POST之类的基本http请求,并且运行良好。现在,我还需要能够下载Excel文件。axios有可能吗?如果是这样,有人可以提供一些示例代码吗?如果没有,我还能在React应用程序中使用什么来做同样的事情?


我们可以使用此解决方案下载excel文件。stackoverflow.com/questions/57127361/...
美龄Nazrul回教

Answers:


107

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

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

希望这可以帮助 :)


1
谢谢。你能告诉我这是否是ajax风格的。最好不要阻止该页面。
David Choi

是的,Page将不会被阻止。当您将url作为属性传递给该组件时,文件将自动下载。您无需执行任何操作。
Hardik Modha

还有一个问题。就我而言,正在下载的文件是通过一些参数动态创建的。因此,它实际上并没有一致的位置。我为这种情况发送的网址是什么?例如,如果我叫axios.post('api / getmyexcelfile',params);
David Choi

如在答案中提到的。在axios响应对象中,请求内有一个名为As的字段responseURL,也许这是您想要的URL。
Hardik Modha

1
我遵循了这一点,并能够下载文件。但是文件已损坏(无效)。但是,如果我使用重定向(window.location.href),则该文件将下载并正常运行。有人可以帮我吗?(stackoverflow.com/questions/56306008/...
Thidasa Pankaja

125

更一般的解决方案

axios({
  url: 'http://api.dev/file-download', //your url
  method: 'GET',
  responseType: 'blob', // important
}).then((response) => {
   const url = window.URL.createObjectURL(new Blob([response.data]));
   const link = document.createElement('a');
   link.href = url;
   link.setAttribute('download', 'file.pdf'); //or any other extension
   document.body.appendChild(link);
   link.click();
});

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

完整学分至:https : //gist.github.com/javilobo8


12
谢谢您的解决方案。谨为其他人提供一些注意事项:虽然这在许多用例中都可行,但是对于大文件,您将无法看到下载进度。并且它将在浏览器中占用额外的内存。正如在其他解决方案中提到的一样,但没有明确指出,一般的方法是使用标头“ Content-Disposition:Attachment;”。因此浏览器会将其视为本机下载(上述下载进度+直接下载到磁盘)。
约翰·李

在服务器端,即使我设置了Content-Desposition标头,似乎也不允许下载进度。
huggie

4
谢谢你 想知道为什么文件内容没有正确显示。原来我不见了responseType: 'blob'
AliAvci

不此下载的文件先存储的响应(而不实际显示下载进度的浏览器),只有当文件被下载在内存中的BLOB那么只有在浏览器将尝试将它保存到下载文件..
瑞奇-U

@ Ricky-U是的,您是正确的,将发送xhr请求,并且当响应到达时,它将被缓冲在内存中,然后response在完成时存储在变量中。然后createObjectURL为<a>可以导航到的该数据创建一个本地URL。
维尼

50

下载文件(使用Axios和Security)

当您想使用Axios和某些安全性方法下载文件时,这实际上甚至更加复杂。为了防止其他人花太多时间来解决这个问题,让我逐步指导您。

您需要做三件事:

1. Configure your server to permit the browser to see required HTTP headers
2. Implement the server-side service, and making it advertise the correct file type for the downloaded file.
3. Implementing an Axios handler to trigger a FileDownload dialog within the browser

这些步骤大部分是可行的-但由于浏览器与CORS的关系而变得相当复杂。一步一步来:

1.配置您的(HTTP)服务器

当采用传输安全性时,在浏览器中执行的JavaScript可以(通过设计)只能访问HTTP服务器实际发送的6个HTTP标头。如果我们希望服务器为下载建议一个文件名,则必须通知浏览器“允许” JavaScript被授予访问其他标题的权限,在该标题中将传输建议的文件名。

为了便于讨论,让我们假设我们希望服务器在名为X-Suggested-Filename的HTTP标头中传输建议的文件名。HTTP服务器告诉浏览器,这是确定该收到的自定义标题暴露给JavaScript /爱可信与下面的头:

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

根据产品的不同,配置HTTP服务器以设置此标头的确切方法也有所不同。

有关这些标准标头的完整说明和详细说明,请参见https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers

2.实施服务器端服务

您的服务器端服务实现现在必须执行两件事:

1. Create the (binary) document and assign correct ContentType to the response
2. Assign the custom header (X-Suggested-Filename) containing the suggested file name for the client

根据您选择的技术堆栈,可以通过不同的方式完成此操作。我将使用JavaEE 7标准绘制一个示例,该示例应发出一个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();
}

现在,该服务会发出二进制文档(在这种情况下为Excel报告),设置正确的内容类型-并发送包含建议的文件名的自定义HTTP标头,以供保存文档时使用。

3.为接收的文档实现Axios处理程序

这里有一些陷阱,所以让我们确保正确配置所有细节:

  1. 服务响应@GET(即HTTP GET),因此axios调用必须为“ axios.get(...)”。
  2. 该文档以字节流的形式传输,因此您必须告诉axios将响应视为HTML5 Blob。(即responseType:'blob')。
  3. 在这种情况下,文件保护JavaScript库用于打开浏览器对话框。但是,您可以选择另一个。

然后,基本的Axios实现将类似于以下内容:

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

21
什么是“ FileSaver”?
主朋友

6
这是一个处理下载文件的库,github.com/eligrey/FileSaver.js/#filesaverjs
Radi

3
此方法有效,但建议使用content-dispositionheader而不是x-suggested-filename
Rosdi Kasim

14

IE和其他浏览器的Axios.post解决方案

我在这里找到了一些不可思议的解决方案。但是他们经常不考虑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")

9

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

8
        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.
})

7

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

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

您不需要/不需要axios进行此操作;只允许浏览器执行此操作应该是标准的。

注意:如果您需要下载授权,则可能无法正常工作。我很确定您可以使用Cookie来授权这样的请求,前提是该请求位于同一域中,但是无论如何,在这种情况下这可能无法立即生效。


至于是否可能...内置文件下载机制不行,否


12
授权标头?
Ejaz Karim

如果您需要发送令牌怎么办?
user3808307 '20

如果控制服务器,则只需将访问令牌存储为cookie,浏览器就会将其添加到对服务器的任何请求中。medium.com/@ryanchenkie_40935/…–
Multihunter

1
@CharithJayasanka是的,我相信。
Multihunter

2

诀窍是在中创建一个不可见的锚标记,render()并添加一个React,ref以便在收到axios响应后触发点击:

class Example extends Component {
    state = {
        ref: React.createRef()
    }

    exportCSV = () => {
        axios.get(
            '/app/export'
        ).then(response => {
            let blob = new Blob([response.data], {type: 'application/octet-stream'})
            let ref = this.state.ref
            ref.current.href = URL.createObjectURL(blob)
            ref.current.download = 'data.csv'
            ref.current.click()
        })
    }

    render(){
        return(
            <div>
                <a style={{display: 'none'}} href='empty' ref={this.state.ref}>ref</a>
                <button onClick={this.exportCSV}>Export CSV</button>
            </div>
        )
    }
}

这是文档:https : //reactjs.org/docs/refs-and-the-dom.html。您可以在这里找到类似的想法:https : //thewebtier.com/snippets/download-files-with-axios/


0

这为我工作。我在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 });
})

-1

对于axios POST请求,该请求应类似于以下内容:此处的关键是responseTypeandheader字段必须位于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;
}

-4

我的答案是完全破解-我刚刚创建了一个看起来像按钮的链接,并在其中添加了URL。

<a class="el-button"
  style="color: white; background-color: #58B7FF;"
  :href="<YOUR URL ENDPOINT HERE>"
  :download="<FILE NAME NERE>">
<i class="fa fa-file-excel-o"></i>&nbsp;Excel
</a>

我使用的是出色的VueJ,因此使用了奇怪的注释,但是,此解决方案与框架无关。这个想法适用于任何基于HTML的设计。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.