37

I am working on a Vue application with a Laravel back-end API. After clicking on a link I would like to do a call to the server to download a certain file (most of the time a PDF file). When I do a get request with axios I get a PDF in return, in the body of the response. I would like to download that file directly.

To give you a better view of how the response is looking like:

enter image description here (note: I know a real text response is better than an image but I don't see any way to return that because of the length of the actual PDF content..)

Is there any way of downloading that file with JavaScript or something? It has to be specific a direct download without clicking on the button again.

Code

// This method gets called when clicking on a link
downloadFile(id) {
    const specificationId = this.$route.params.specificationId;

    axios
        .get(`${this.$API_URL}/api/v1/suppliersmanagement/product-specifications/${specificationId}/fileupload/${id}/download`, {
            headers: this.headers,
        })
        .then(response => {
            console.log(response);
            // Direct download the file here..
        })
        .catch(error => console.log(error));
},
Hyyan Abo Fakher
  • 3,497
  • 3
  • 21
  • 35
Giesburts
  • 6,879
  • 15
  • 48
  • 85
  • 1
    Take a look at [this link](https://weblog.west-wind.com/posts/2007/May/21/Downloading-a-File-with-a-Save-As-Dialog-in-ASPNET). While it is in asp.net, your server code should set the headers right, for the client to cause a prompt or show pdf. – shahkalpesh Jul 25 '18 at 08:40

6 Answers6

46

As @Sandip Nirmal suggested I've used downloadjs and that worked out pretty good! Had to make a few adjustments to my code but in the end it worked out.

My new code

// npm i downloadjs
import download from 'downloadjs'

// method
downloadFile(file) {
    const specificationId = this.$route.params.specificationId;

    axios
        .get(`${this.$API_URL}/api/v1/suppliersmanagement/product-specifications/${specificationId}/fileupload/${file.id}/download`, {
            headers: this.headers,
            responseType: 'blob', // had to add this one here
        })
        .then(response => {
           const content = response.headers['content-type'];
           download(response.data, file.file_name, content)
        })
        .catch(error => console.log(error));
},
Giesburts
  • 6,879
  • 15
  • 48
  • 85
26

You should use 'responseType' option. For example:

axios.get(
  url, 
  {responseType: 'blob'} // !!!
).then((response) => {
  window.open(URL.createObjectURL(response.data));
})
Slava
  • 1,559
  • 1
  • 12
  • 17
  • 2
    That was it! To correctly receive a pdf from the server, I had to use `responseType: 'arraybuffer'` in the config field of the httpService call. Thank you! – kotchwane Oct 27 '21 at 12:55
8

You have 2 options for this. If you want to do it from server and if you are using Node.js as a backend. You can do it easily using res.download method of express. You can follow this answer for that Download a file from NodeJS Server using Express.

But if you want to handle it from client then there are few options since you can't use axios, XHR, fetch to download file directly. You can either use download.js or write your own code in following way.

return axios({
    url: '/download', // download url
    method: 'get',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      mode: 'no-cors'
    }
  })
    .then(response => response.blob())
    .then(blob => {
      var url = window.URL.createObjectURL(blob)
      var a = document.createElement('a')
      a.href = url
      a.download = fileName
      a.click()
      a.remove()
      setTimeout(() => window.URL.revokeObjectURL(url), 100)
    })

Since response returned from server is in json format you need to convert it into ObjectURL and set it to anchor tag.

If you sneak inside download.js code you will find same implementation.

Marcello B.
  • 4,177
  • 11
  • 45
  • 65
Sandip Nirmal
  • 2,283
  • 21
  • 24
  • As you can read in my post, I don't use Node.js but I use Laravel. Anyway, I will look into download.js – Giesburts Jul 25 '18 at 09:12
  • ``download.js`` does the same exact thing like above mentioned code snippet. – Sandip Nirmal Jul 25 '18 at 09:14
  • 1
    `response.blob is not a function` . // - Browser only: FormData, File, Blob . // - Node only: Stream, Buffer . I'm working on a Node JS CLI tool. I'm trying to replicate the `.blob()` approach with `stream`. – Reuven Etzion Apr 05 '19 at 16:44
  • I was able to use `data.pipe(writer)` and a `writer.on("finish")` listener to save and then serve the file locally in my CLI tool. – Reuven Etzion Apr 05 '19 at 17:33
5

2022 answer: using node.js, fs.promises and async/await

The key is using responseType: 'stream' per the Axios docs.

import axios from 'axios';
import { writeFile } from 'fs/promises';

const downloadFile = async () => {
  const response = await axios.get('https://someurl', {
    params: {
      // ...
    },
    // See https://axios-http.com/docs/api_intro
    responseType: 'stream',
  });
  const pdfContents = response.data;
  await writeFile('file.pdf', pdfContents);
};

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
1

You can do it like this

download(filename) {
  fetch(url , { headers })
  .then(response => response.blob())
  .then(blob => URL.createObjectURL(blob))
  .then(uril => {
    var link = document.createElement("a");
    link.href = uril;
    link.download = filename + ".csv";
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  });
}

here I want to download a CSV file, So I add .csv to the filename.

VINEE
  • 317
  • 3
  • 5
1
const downloadPDF = (id, fileName) => {
    axios({
        method: 'get',
        url: `https://api.example.com/pdf/invoice/${id}`,
        headers: {
            'Authorization': 'Bearer ' + localStorage.getItem('token'),
            'Content-Type': 'application/json'
        },
        responseType: 'blob'
    }).then(function (response) {
        const a = document.createElement('a');
        a.href = window.URL.createObjectURL(response.data);
        a.download = `${fileName}.pdf`;
        document.body.appendChild(a);
        a.click();
        a.remove();
    });
}
DelayMan
  • 11
  • 2
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 08 '23 at 01:35