I am trying to prompt a download of a PDF generated on the server using Node.js. Currently I can save it to the server but not force a download.
I am using npm html-pdf and currently it is saving to the server with this code.
pdf.create(htmlbody).toStream(function(err, stream){
console.log(stream);
if (err) return next(err);
stream.pipe(fs.createWriteStream('./foo.pdf'));
res.status(200);
res.send('Success');
});
How can I make it force a download?
I tried setting headers like
res.setHeader('content-type', 'application/pdf');
res.setHeader("Content-Disposition", "attachment; filename="+pdfName+";");
I am testing on localhost but I don't presume this to be the issue.
You don't have to save it to the server (unless that is a business logic requirement). What you can do is simply this:
pdf.create(htmlbody).toStream(function(err, stream){
if (err) return next(err);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename='+pdfName+';');
stream.pipe(res);
});
Basically, all you need to do is set the appropriate headers and then pipe the stream you get from html-pdf to your res stream.
If you however need to save the PDF on the server first, then you'll have to write it to the file system and once it's done, read it back and send it over the response stream again.
Edit: Here's the code snippet with a full working example (running on Node v4.0.0).
var http = require('http');
var pdf = require('html-pdf');
const PORT = 8765;
var server = http.createServer((req, res) => {
var html = '<html><head><title>PDF Test</title></head><body><h1>Welcome</h1><p>This is a test to see if we can get <b>PDF generation</b> to work on the server side.</p><p>PDF files can be very tricky to do on the server so this will take a bit of <u>hit and trail</u> before we can be confident it works fine.</p></body></html>';
pdf.create(html).toStream((err, stream) => {
if (err) {
console.error(err);
res.status(500);
res.end(JSON.stringify(err));
return;
}
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=test-file.pdf;');
stream.pipe(res);
});
});
server.listen(PORT, () => {
console.log('Server listening on Port: %s', PORT);
});
Save the above as something like pdf.js and run in with node (node pdf.js) and open http://localhost:8765/. This should trigger a download in your browser.
Related
I am trying to send a csv file to the client.
When I do this, the download begins, but hangs on "starting...." and says it is transmitting at 0B/s.
When I kill the server, the file is transmitted and is all there.
What's causing this?
app.get('/weather_log.csv', function(req, res){
console.log('sending weather log')
filePath = 'weather_log.csv'
var file = fs.readFileSync(filePath, 'binary');
console.log(typeof(file))
res.setHeader('Content-Length', fs.statSync(filePath));
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', "attachment;filename=" + filePath);
res.write(file, 'binary');
res.end();
})
My guess is that res.end() is not firing or that the client doesn't realise that the file transfer is not complete for some other reason.
As per a0js, the answer was to use res.download(filePath).
I'm trying to setup some socket.io communications, the communication between my server (app.js)(runs on a raspberry pi) and a website(public/index.html) works fine. Now I want to expand it so when my app.js receives a call from index.html it emits it further to another node.js script(bed.js) that will run on another raspberry pi. I tried to use the npm module socket.io-client, but this can only receive apparently
!edit! problem has narrowed down to the setrgb part, there it won't emit.
!edit 2! when i receive setRGB, i emit setRGBclient, but that can only be received in bed.js, not in index.html, there lays my problem, i need to share the connections or force it to another connection, no clue how i fix it though
APP.JS:
let http = require('http').createServer(handler); //require http server, and create server with function handler()
let fs = require('fs'); //require filesystem module
let io = require('socket.io')(http) //require socket.io module and pass the http object (server)
let delay = require('delay');
console.log('Define each color from RGB Strip light.');
http.listen(8080); //listen to port 8080
function handler (req, res) { //create server
fs.readFile(__dirname + '/public/index.html', function(err, data) { //read file index.html in public folder
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
res.write(data); //write data from index.html
return res.end();
});
}
io.sockets.on('connection', function (socket) {// WebSocket Connection
socket.on("test", function(){
console.log("sampletext");
});
socket.on("setRGB", function(data){
socket.emit("setRGBClient", data);
console.log(data);
console.log("test");
});
});
bed.js:
let socket = require('socket.io-client')('http://localhost:8080');
let lightstate = false;
let stayOff = false;
let fadeState = false;
console.log("check");
socket.emit("test");
socket.on("setRGBClient" ,function(data) {
console.log(data);
});
I can just broadcast setRGBClient.
socket.broadcast.emit("setRGBClient", data);
I guess this is a learning exercise. Otherwise I’d caution against socket.io for such applications.
However I can only see the subscription for ‘setRGB’ not the emit-part.
I want to download an image from node server when sending post request for image file name.But I couldn't get either save dialog box or direct download.I have a folder uploads from which I want to read the image and download.I am trying the code pasted over here.Please help me with a good solution.
router.post('/download', urlencodedParser, function(req, res) {
var img = path.resolve('uploads/' + req.body.file);
var filename = path.basename(img);
var mimetype = mime.lookup(img);
console.log(filename);
console.log(mimetype);
res.setHeader('Content-disposition', 'attachment; filename=' + filename);
res.setHeader('Content-type', mimetype);
var filestream = fs.createReadStream(img);
filestream.pipe(res);
res.on('finish', function(){
console.log("Download complete");
});
})
Currently, I have a lot of linux based clients downloading firmware updates from my webserver.
After the client has successfully downloaded the firmware file, my server needs to execute a few scripts, which logs in to the client and performs some tasks.
Is there a way for a node server to keep track of the clients download progress, so I can execute the needed scripts once the file has been downloaded?
Ok so I will try.
If you serve your Firmware as static files through Apache/Nginx and direct url call. You don't get the progress inside your NodeJS script.
If you serve your files via stream inside a express controller you can listen to the progress. Look at this answer here https://stackoverflow.com/a/42273080/3168392
You will have to use a socket connection to make sure the node server gets update from the client of the progress of the file being downloaded.
Something like this
CLIENT_CODE
var socket = io('http://localhost');
socket.on('connect', function(){});
socket.on('data_reciving', parts.pop(),function(percentage){
if(parse_data(percentage) === 100){
client.socket.emit('downloadCompleted', {fileName:'test'});
}else{
//do nothing
}
});
SERVER_CODE:
sockets.on('connection', function (socket) {
//listen to the event from client
socket.on('downloadCompleted', function (data) {
connect_to_client();
do_some_operation();
socket.emit('ALLDONE',{some_data});
});
});
I guess this helps ,you can use this post for reference.
If you just want to run some code when a download has finished, you can use on-finished:
const onFinished = require('on-finished');
app.use((req, res, next) => {
onFinished(res, (err, res) => {
...log some data, perform some housekeeping, etc...
});
next();
});
As is, it will attach a "finished" listener to all responses, which is probably not what you want. Since this is plain Express middleware, you can attach it to specific routes instead (but how depends on how exactly the files are being served).
I found some code that seems to fit my needs.
With the code below, I can detect both the progress of a user's download from the server-side, and fire an event once the file transfer completes.
var http = require("http");
var fs = require("fs");
var filename = "./large-file";
var serv = http.createServer(function (req, res) {
var sent = 0;
var lastChunkSize = 0;
var stat = fs.statSync(filename);
res.setHeader('Content-disposition', 'attachment; filename=large-file.iso');
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Keep-Alive', 'timeout=5, max=100');
res.writeHeader(200, {"Content-Length": stat.size});
var fReadStream = fs.createReadStream(filename, { highWaterMark: 128 * 1024 });
fReadStream.on('data', function (chunk) {
if(!res.write(chunk)){
fReadStream.pause();
lastChunkSize = chunk.length;
console.log('Sent', sent, 'of', stat.size);
}
});
fReadStream.on('end', function () {
console.log('Transfer complete.');
res.end();
});
res.on("drain", function () {
sent += lastChunkSize;
fReadStream.resume();
});
});
serv.listen(3001);
I'm trying to create a Node.js HTTP file streaming server. I want to do the following:
sender --HTTP POST--> Node.js HTTP Server <--HTTP GET-- receiver
I have this already working with the following code:
var app = express();
// ...
app.post('/upload', function (req, res) {
// Get the `req` and `res` arguments of the HTTP GET request
var downloadRequest = getDownloadClientRequest();
// parse a file upload
var form = new formidable.IncomingForm();
form.onPart = function (part) {
if (!part.filename) {
// let formidable handle all non-file parts
form.handlePart(part);
return;
}
downloadRequest.res.writeHead(200, {
'Content-Disposition': 'attachment; filename=' + part.filename
});
part.on('data', function(data) {
downloadRequest.res.write(data);
});
part.on('end', function() {
downloadRequest.res.end();
});
};
form.parse(req, function (err, fields, files) {
res.writeHead(200);
res.write('Successfully streamed');
res.end();
});
});
app.get('/download', function (req, res) {
storeDownloadClientRequest(req, res);
});
What obviously happens is that if the uploading client is much faster than the downloading client, the upload completes very fast and my Node.js process keeps the whole file in memory until the downloading client finishes it.
How can I throttle the uploader's speed? I guess I can pause his stream every now and then but how do I know if the downloader's stream/buffer is filling up?
PS: I throttled the downloading client with wget --limit-rate=100k, if it helps.
part.pipe(downloadRequest) should automatically handle pausing/resuming when buffer is full.