Node js export generated json as file

2019-09-08 09:50发布

问题:

My node server is generating a json object/array. I need to send it to the user, but not to display on the browser, but as a file. How do I do it?

I dont think writing it to a file and then using res.sendFile() would be a right thing to do.

回答1:

You just need to add header Content-Type as something that browser won't use to display it directly, then content would be downloaded as attachment.

If you want to name the download file then you'll have to use header Content-Disposition.

JSON object can simply be converted to string and sent with above two headers.

Following is the working server code.

http = require('http');
server = http.createServer( function(req, res) {

    /**************** This is the important part **************/
    res.writeHead(200, {
        'Content-Type': 'application/json-my-attachment',
        "content-disposition": "attachment; filename=\"my json file.json\""
    });

    var jsonObj = {"name":"Will McAvoy"};
    res.end(JSON.stringify(jsonObj));
});

port = 3000;
host = '127.0.0.1';
server.listen(port, host);
console.log('Listening at http://' + host + ':' + port);

application/json-my-attachment is a made up Content-Type name, since browser does not recognize it, it will try to download it. Otherwise you can also add standard header Content-Type: application/octet-stream.

Edit

Regarding your second question, how to send parameters in POST request when downloading file, it can be done easily using HTML Form.

As you're using AngularJS, I'm assuming that you don't want your page to refresh by submitting the form. This can be done using an IFrame. Just update the IFrame's src attribute with Javascript to file download path.

It solves the page refresh problem but we can not send POST request with body using just an IFrame.

Here we have to use a combination of Form and IFrame. HTML Form has an attribute target in which we can specify the name of an IFrame. Form can specify the form parameters, action and HTTP method to use. Since the target is an IFrame it won't refresh the current page.

Note: Limitation of this method is that you can not send HTTP headers with the form submission though there are some hacks available.

Following is working code. I have used Expressjs as the node server.

HTML

<button id="downloadFile">download file</button>
<form id="downloadForm" method="post" action="http://localhost:3000/download" target="downloadIframe" style="display:none">
    <input type="text" name="param1">
    <input type="text" name="param2">
</form>
<iframe id="downloadIframe" name="downloadIframe" style="display:none"/>

Javascript

$(function () {
    $("#downloadFile").click(function () {
        var form = $("#downloadForm");
        form.find("[name=param1]").val("hello");
        form.find("[name=param2]").val("world");
        form.submit();
    });         
});

NPM Installation

npm install express
npm install body-parser

Node Server

var express = require('express');
var bodyParser = require('body-parser');
var path = require('path');
var fs = require('fs');

app = express(),
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

port = process.env.PORT || 3000;

app.all("/*", function (req, res) {
    if(req.method = "POST" && req.url === "/download") {
        /**************** This is the important part **************/
        res.writeHead(200, {
            'Content-Type': 'application/json-my-attachment',
            "content-disposition": "attachment; filename=\"my json file.json\""
        });
        console.log("body : " + JSON.stringify(req.body));
        var jsonObj = {"name":"Will McAvoy"};
        res.end(JSON.stringify(jsonObj));
    } else {

        var filePath = req.url;
        filePath = (filePath === "/") ? "index.html" : filePath;
        filePath = path.join(__dirname, "public", filePath);
        console.log(filePath);
        fs.stat(filePath, function (err, stat) {
            if(!err) {
                res.sendFile(filePath);
            } else {
                res.writeHead(404);
                res.end("file not found");
            }
        });
    }
});

console.log("server started at : http://localhost:" + port);
app.listen(port);

** Server output on download button click**

server started at : http://localhost:3000
body : {"param1":"hello","param2":"world"}