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.
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.
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"}