64

I'm using vuejs 2 + axios. I need to send a get request, pass some params to server, and get a PDF as a response. Server uses Laravel.

So

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

makes successful request but it does not start force downloading, even though server returns correct headers.

I think this is a typical situation when you need to, say, form a PDF report and pass some filters to server. So how could it be accomplished?

Update

So actually I found a solution. However the same approach didn't work with axios, don't know why, that's why I used raw XHR object. So the solution is to create a blob object and user createUrlObject function. Sample example:

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

Important: you should have array buffer as response type

However, the same code written in axios returns PDF which is empty:

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
})
Vadym
  • 1,444
  • 21
  • 37
Victor
  • 5,073
  • 15
  • 68
  • 120
  • Any updates on this? – Deepak Apr 17 '17 at 17:07
  • Yep, actually you can save file to your disk. I'll post solution which I came up with – Victor Apr 17 '17 at 21:54
  • Interesting, please post. And I also came across this, in vue, a component can be created which manages this. http://stackoverflow.com/questions/16799483/using-jquery-and-iframe-to-download-a-file – Deepak Apr 18 '17 at 08:26
  • I suspecd axios encode response.data for transport, I tried to return the base64 encoded pdf, which can be downloaded normally – tacy_lee Sep 07 '17 at 08:13
  • I've noticed you initially tried with `GET` method but ended up doing `POST` method. Is there any particular reason for it? – smchae May 04 '18 at 10:03

6 Answers6

98

You're getting empty PDF 'cause no data is passed to the server. You can try passing data using data object like this

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

By the way I gotta thank you so much for showing me the hint in order to download pdf from response. Thank ya :)

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

The way in which I have used is,

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()
})
Vadym
  • 1,444
  • 21
  • 37
Roshimon
  • 1,991
  • 19
  • 16
  • 10
    Great solution. I have put `responseType:'blob'` and the response.data contained the blob directly. – Ciprian Rarau Jan 18 '18 at 15:47
  • this works great, as far as it goes. Now, how to use the filename that is coming from the server? `content-disposition: attachment; filename="My Test - Doc Title.pdf"` – Old Man Walter Sep 27 '19 at 21:02
  • im getting `Uncaught (in promise) Error: Request failed with status code 500`, how can i fix this please? – t q Sep 23 '20 at 21:48
16

Try this: It works perfectly for me with compatibility for Internet Explorer 11 (createObjectURL doesn't work on 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

gtamborero
  • 2,898
  • 27
  • 28
6

I tried some of the above suggested approaches but in my case the browser was sending me the popup block warning. The code described below worked for me:

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();
});
Bruno Soares
  • 484
  • 5
  • 7
5

I don't think its possible to do this in axios or even AJAX. The file will be kept in memory, i.e. you cannot save file to disk. This is because JavaScript cannot interact with disk. That would be a serious security issue and it is blocked in all major browsers.

You can construct your URL in front-end and download it in the following way:

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

 window.open(url, '_blank');

Hope this helps!

Deepak
  • 1,342
  • 12
  • 20
  • What if I need to send post request? Because probably the request params will be too big for get – Victor Apr 16 '17 at 09:06
  • No matter what, you wont be able to save the file to your disk. GET request can allow 2048 characters. – Deepak Apr 16 '17 at 09:10
0

this code works for me :

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

I had similar issues- I ended up creating link and downloading from there.

I put more details of how in the answer on another stackoverflow question.

Anthony
  • 487
  • 1
  • 6
  • 21