简体   繁体   中英

Express.JS response.download not downloading file

I am working on a Node.JS server using Express to generate and download PDFs based on user input. I call the server using Axios POST, and then am expecting the file to be downloaded using response.download() from Express. I recently changed from using the <form action=""> method of calling my API to Axios because Netlify doesn't seem to support NuxtAPI. The program produced the desired files in the old version, however something about the change to Axios means that it no longer works.

Now, no file is being downloaded (even fixed-path ones such as on published websites), and the page is reloading before even being prompted for the download.

My code is as below:

print.vue:


<template>
<main>
     <button class="btn btn-primary" @click="submit">Print Certificate</button>
</main>
</template>
<script>
export default{
methods:{
        async submit(){
            try{
                let params = {
                    name: this.name,
                    kana: this.kana,
                    rank: this.rank,
                    date: this.date
                }
                // produces valid object in all cases
                await this.$axios.$post('/api/certificate', null, {params})
            }catch(err){
                console.log(err)
                alert(err)
            }
        }
    },
    computed:{ // functions from route params, resolve to these always for test case
    name: function(){return 'Test Student'},
    kana: function(){return "Tesuto Sutuudento"},
    rank: function(){return "8th Kyu"},
    date: function(){return '26th October, 2020"}
    }
}
</script>

/api/certificate.js

var urlencodedParser = bodyParser.urlencoded({ extended: false })
app.post('/', urlencodedParser, function(req, res) {
    let response = {
      name: req.query.name,
      kana: req.query.kana,
      rank: req.query.rank,
      date: req.query.date
    }
    console.log(response)
    html = html.replace("{{name}}", response.name)
    html = html.replace("{{kana}}", response.kana)
    html = html.replace("{{rank}}", response.rank)
    html = html.replace("{{date}}", response.date) // always produces valid string (template)
    pdf.create(html, options).toFile('static/certificates/' + response.name.split(' ')[0] + '_' + response.rank.split(' ')[0] + '_' + response.rank.split(' ')[1] + '.pdf', function(err, rep){
        // file made with no difficulties
        if (err) return console.log(err);
        console.log(rep.filename) // gives absolute path (C:\\)
        //res.download('https://get.pxhere.com/photo/computer-screen-technology-web-internet-signage-page-coding-website-html-programming-font-design-text-digital-information-java-site-games-software-development-code-screenshot-data-script-www-css-computer-programming-web-developer-web-development-programming-code-web-page-website-development-670370.jpg') // test fixed path downloading
        //res.download(rep.filename,'my_pdf.pdf') // what I think the easiest solution should be
        res.download('static/certificates/' + response.name.split(' ')[0] 
        + '_' + response.rank.split(' ')[0] + '_' 
        + response.rank.split(' ')[1] + '.pdf') // what worked before
    })
})

I'm relatively new to APIs and Node in general, so is there something fundamental that I'm missing?

It turns out that Axios doesn't like downloading anything besides TXT, HTML, or JSON files, so we need to download the produced file as an arraybuffer, and then use js-file-download to turn it into our desired file format.

Example below:

in my server

import Path from 'path'
import pdf from 'html-pdf'

    pdf.create(html, options).toFile('./mypdf.pdf', function(err, rep){
        if (err) return console.log(err);
        let path = Path.resolve(rep.filename) // ensures we always get the generated path
        console.log("resoved path:" + path)
        res.setHeader('Content-disposition', 'mypdf.pdf');
        res.download(path)

    })

In my client:

methods:{
        async submit(){
            const fileDownloader = require('js-file-download');
            try{
                let params = {
                    name: this.name,
                    kana: this.kana,
                    rank: this.rank,
                    date: this.date
                }
                // use responseType to tell axios its a buffer, using $post instead of post is shorthand for post().data
                // not setting this header results in corrupted pdfs
                let response = await this.$axios.$post('/api/certificate', null, {params: params, responseType:'arraybuffer'})
                console.log(response) // gibberish because Adobe
                // create and immediately download. Since we don't want to give a choice, immediate download is fine 
                fileDownloader(response, 'mypdf.pdf') 
                
            }catch(err){
                console.log(err)
                alert(err)
            }
        }
    },

Special thanks to this answer for client-side headers, this answer for a link to js-file-downloader and this answer for server-side response headers. Wouldn't have thought it was this tricky.

I think this will work

know more about writeFileSync


const data = {
    Patient_name: result.patient_name,
    Doctor: result.doctor,
    Date: result.date,
    Time: result.time,
    Key: result.key
};
const path = `${process.cwd()}/public/UserData.csv`;
fs.writeFile(path, data, function (err) {
    if (err) { throw err; }
    else {
        res.download(path);
        console.log(data);
    }
})

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