简体   繁体   中英

How to trigger a browser download from a browser FETCH?

I'm working on a project with Vue.js and Typescript for front-end and Java Spring as backend.

My java controller retrieves a given report from the db and then copies it into the HTML response. I want the CSV to be downloaded by the browser therefore I added Content-disposition header in the response.

     @GetMapping('/download')
     public void downloadCSV(HttpServletRequest response){
        Report r = reportService.findById(85);
        response.addHeader("Content-Disposition", "attachment; filename=myCSV.csv");
        response.setContentType("text/csv");

        try {
            InputStream stream = new ByteArrayInputStream(r.getDocument());
            IOUtils.copy(stream, response.getOutputStream());
            response.flushBuffer();
        } catch(Exception e) {...}
     }

I have 2 buttons: one simple hyperlink tag with href linking to download() , and a b-button (from bootstrap-vue) that once cliccked triggers download2() .

   <a :href="download" role="button"> Download CSV </a>

   <b-button @click="event => download2()">
      Download CSV v2
   </b-button>
    get download(): string {
        return 'http://localhost:8080/download';
    }


    async download2() {
        const rHeaders = new Headers();
        rHeaders.append('Accept', 'text/csv');

        const configInit = RequestInit = {
           method: 'GET',
           headers: rHeaders
        };

        try {
            const res = await fetch('http://localhost:8080/download', configInit);
            return res.text();
        } catch (e) {...}
    }

Now, If I click on the first button "Download csv", the csv is correctly downloaded by the browser. The javascript console prints the following:

Resource interpreted as Document but transferred with MIME type text/csv

and there is nothing in the response body.

Instead, if I click the 2nd button "Download csv v2", the download does not start but I have the csv in the response body.

Here, the differences in the request headers between the two.

*Header*                   *Download csv*        *Download csv v2*
Sec-Fetch-Dest                document               empty
Sec-Fetch-Mode                navigate               cors
Sec-Fetch-User                   ?1                    -
Upgrade-Insecure-Requests        1                     -

the other headers are the same. It is not possible to change these headers, even if I set them in the javascript method; they remain still the same. What's the problem? Thank you.

I found a solution by "mimicking" the behavior of a <a> element:

This way, it works properly:

async download2() {
    const configInit: RequestInit = {
        method: 'GET'
    };

    try {
       await fetch('http://localhost:8080/download', configInit)
          .then(response => response.blob())
          .then(blob => {
              const url = window.URL.createObjectURL(blob);
              const a = document.createElement('a');
              a.style.display = 'none';
              a.href = url;
              a.download = 'report.csv';
              document.body.appendChild(a);
              a.click();
              window.URL.revokeObjectURL(url);
          })
    } catch (e) {...}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM