使用axios强制下载GET请求

65

我正在使用vuejs 2 + axios。 我需要发送一个get请求,将一些参数传递给服务器,然后获取PDF作为响应。 服务器使用Laravel。

因此

axios.get(`order-results/${id}/export-pdf`, { params: { ... }})

成功发出请求,但不会启动强制下载,即使服务器返回正确的标头。

我认为这是一个典型的情况,当你需要生成PDF报告并将一些筛选器传递给服务器时。那么该如何实现呢?

更新

事实上,我找到了解决方案。然而,相同的方法在使用axios时无效,不知道为什么,所以我使用了原始的XHR对象。因此,解决方案是创建一个Blob对象并使用createUrlObject函数。示例:

let xhr = new XMLHttpRequest()
xhr.open('POST', Vue.config.baseUrl + `order-results/${id}/export-pdf`, true)
xhr.setRequestHeader("Authorization", 'Bearer ' + this.token())
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
xhr.responseType = 'arraybuffer'

xhr.onload = function(e) {
  if (this.status === 200) {
    let blob = new Blob([this.response], { type:"application/pdf" })
    let link = document.createElement('a')
    link.href = window.URL.createObjectURL(blob)
    link.download = 'Results.pdf'
    link.click()
  }
}

重要提示:您应该将数组缓冲区设为响应类型

然而,使用axios编写的相同代码返回了空白PDF:

axios.post(`order-results/${id}/export-pdf`, {
  data,
  responseType: 'arraybuffer'
}).then((response) => {
  console.log(response)

  let blob = new Blob([response.data], { type: 'application/pdf' } ),
      url = window.URL.createObjectURL(blob)

  window.open(url); // Mostly the same, I was just experimenting with different approaches, tried link.click, iframe and other solutions
})

这个有更新了吗? - Deepak
是的,实际上您可以将文件保存到您的硬盘。我会发布我想出的解决方案。 - Victor
有趣,请发帖。我还发现在Vue中可以创建一个组件来管理它。https://dev59.com/umQn5IYBdhLWcg3we3HD - Deepak
我怀疑axios对response.data进行了编码以进行传输,我尝试返回base64编码的PDF文件,可以正常下载。 - tacy_lee
我注意到你最初尝试使用了 GET 方法,但最终使用了 POST 方法。有什么特别的原因吗? - smchae
6个回答

99

你得到了空的PDF,因为没有数据传递给服务器。你可以尝试使用类似下面的数据对象传递数据。

  axios
    .post(`order-results/${id}/export-pdf`, {
      data: {
        firstName: 'Fred'
      },
      responseType: 'arraybuffer'
    })
    .then(response => {
      console.log(response)

      let blob = new Blob([response.data], { type: 'application/pdf' }),
        url = window.URL.createObjectURL(blob)

      window.open(url) // Mostly the same, I was just experimenting with different approaches, tried link.click, iframe and other solutions
    })

顺便说一下,非常感谢你给我提示,让我知道如何从响应中下载PDF。谢谢!

                var dates = {
                    fromDate: 20/5/2017,
                    toDate: 25/5/2017
                }

我使用的方式是,

axios({
  method: 'post',
  url: '/reports/interval-dates',
  responseType: 'arraybuffer',
  data: dates
}).then(function(response) {
  let blob = new Blob([response.data], { type: 'application/pdf' })
  let link = document.createElement('a')
  link.href = window.URL.createObjectURL(blob)
  link.download = 'Report.pdf'
  link.click()
})


10
很好的解决方案。我已经添加了 responseType:'blob' ,并且响应数据 response.data 直接包含了 Blob 对象。 - Ciprian Rarau
1
这个很好用,就目前而言。现在,如何使用来自服务器的文件名呢?content-disposition: attachment; filename="My Test - Doc Title.pdf" - Old Man Walter
我收到了 Uncaught (in promise) Error: Request failed with status code 500 的错误信息,请问我该如何修复它? - t q

16
尝试这个: 它对我来说非常完美,同时兼容Internet Explorer 11(createObjectURL在Explorer 11上不起作用)
axios({
  url: 'http://vvv.dev',
  method: 'GET',
  responseType: 'blob', // important
}).then((response) => {
  if (!window.navigator.msSaveOrOpenBlob){
    // BLOB NAVIGATOR
    const url = window.URL.createObjectURL(new Blob([response.data]));
    const link = document.createElement('a');
    link.href = url;
    link.setAttribute('download', 'download.pdf');
    document.body.appendChild(link);
    link.click();
  }else{
    // BLOB FOR EXPLORER 11
    const url = window.navigator.msSaveOrOpenBlob(new Blob([response.data]),"download.pdf");
  }
});

https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743


responseType: 'blob' 是关键部分!谢谢 - Akshay Lokur

6

我尝试了上面提到的一些方法,但在我的情况下,浏览器会发送弹出窗口阻止警告。

以下代码对我有用:

axios.get(url, {responseType: 'arraybuffer'})
   .then(function (response) {
     var headers = response.headers();
     var blob = new Blob([response.data],{type:headers['content-type']});
     var link = document.createElement('a');
     link.href = window.URL.createObjectURL(blob);
     link.download = "Your_file_name";
     link.click();
});

5

我认为在axios甚至AJAX中都不可能实现这一点。文件将保存在内存中,即您无法将文件保存到磁盘上。这是因为JavaScript无法与磁盘进行交互。这将是一个严重的安全问题,并且在所有主要浏览器中都被阻止。

您可以在前端构建URL,并以以下方式下载它:

 var url = 'http://example.com/order-results/' + id + '/export-pdf?' + '..params..' 

 window.open(url, '_blank');

希望这能对你有所帮助!

如果我需要发送POST请求怎么办?因为请求参数可能太大了,无法使用GET。 - Victor
无论如何,您都无法将文件保存到磁盘上。GET请求可以允许2048个字符。 - Deepak

0

这段代码对我有效:

let xhr = new XMLHttpRequest()
xhr.open('POST', Vue.config.baseUrl + `order-results/${id}/export-pdf`, true)
xhr.setRequestHeader("Authorization", 'Bearer ' + this.token())
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
xhr.responseType = 'arraybuffer'
xhr.send()
xhr.onload = function(e) {
if (this.status === 200) {
    let blob = new Blob([this.response], { type:"application/pdf" })
    let link = document.createElement('a')
    link.href = window.URL.createObjectURL(blob)
    link.download = 'Results.pdf'
    link.click()
}

}


-1

我曾经遇到过类似的问题- 最终我创建了链接并从那里下载。

我在另一个stackoverflow问题的答案中提供了更多详细信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接