I have a a node.js server that serves an index.html with a text input for a password. After a serverside password check the download should start for the client. The client shouldn't be able to see the location path where the file lies on the server.
here is my server.js:
var
http = require('http'),
qs = require('querystring'),
fs = require('fs') ;
console.log('server started');
var host = process.env.VCAP_APP_HOST || "127.0.0.1";
var port = process.env.VCAP_APP_PORT || 1337;
http.createServer(function (req, res) {
if(req.method=='GET') {
console.log ( ' login request from ' + req.connection.remoteAddress );
fs.readFile(__dirname +'/index.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
} // method GET end
else{ // method POST start
console.log('POST request from ' + req.connection.remoteAddress);
var body = '';
req.on('data', function (data) {
body += data;
if (body.length > 500) {
// FLOOD ATTACK OR FAULTY CLIENT, NUKE REQUEST
req.connection.destroy(); console.log('too much data')}
});
req.on('end', function () {
var postdata = qs.parse(body);
var password = postdata.passwordpost ;
if (password == '7777777') {
console.log('the password is right, download starting');
// ??????????????????????????????????? here I need help from stackoverflow
}
else{
console.log ('password wrong');
fs.readFile(__dirname +'/wrongpassword.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
}
}); // req on end function end
}
}).listen(port, host);
the part where I need help is marked with ????????
here is my index.html:
<html>
<body>
<br> <br>
please enter your password to start your download
<br> <br>
<form method="post" action="http://localhost:1337">
<input type="text" name="passwordpost" size="50"><br><br>
<input type="submit" value="download" />
</form>
</body>
</html>
Do you know how to do this?
Sure, you can use this in your code :
res.setHeader('Content-disposition', 'attachment; filename='+filename);
//filename is the name which client will see. Don't put full path here.
res.setHeader('Content-type', 'application/x-msdownload'); //for exe file
res.setHeader('Content-type', 'application/x-rar-compressed'); //for rar file
var file = fs.createReadStream(filepath);
//replace filepath with path of file to send
file.pipe(res);
//send file
You needs to declare and require the path: path = require("path")
then can do:
var uri = url.parse(request.url).pathname
, filename = path.join(process.cwd(), uri);
path.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return;
}
response.writeHead(200);
response.write(file, "binary");
response.end();
}
check these complete example.
I found some additional information about fs.createReadStream() ( especially error handling ) here and combined it with the answer of user568109. Here is my working downloadserver:
var
http = require('http'),
qs = require('querystring'),
fs = require('fs') ;
console.log('server started');
var host = process.env.VCAP_APP_HOST || "127.0.0.1";
var port = process.env.VCAP_APP_PORT || 1337;
http.createServer(function (req, res) {
if(req.method=='GET') {
console.log ( ' login request from ' + req.connection.remoteAddress );
fs.readFile(__dirname +'/index.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
} // method GET end
else{ // method POST start
console.log('POST request from ' + req.connection.remoteAddress);
var body = '';
req.on('data', function (data) {
body += data;
if (body.length > 500) {
// FLOOD ATTACK OR FAULTY CLIENT, NUKE REQUEST
req.connection.destroy(); console.log('too much data')}
});
req.on('end', function () {
var postdata = qs.parse(body);
var password = postdata.passwordpost ;
if (password == '7777777') {
console.log('the password is right, download starting');
res.setHeader('Content-disposition', 'attachment; filename='+'test1.exe');
//filename is the name which client will see. Don't put full path here.
res.setHeader('Content-type', 'application/x-msdownload'); //for exe file
res.setHeader('Content-type', 'application/x-rar-compressed'); //for rar file
var readStream = fs.createReadStream('/test1.exe');
//replace filepath with path of file to send
readStream.on('open', function () {
// This just pipes the read stream to the response object (which goes to the client)
readStream.pipe(res);
});
// This catches any errors that happen while creating the readable stream (usually invalid names)
readStream.on('error', function(err) {
console.log (err) ;
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('an error occured', 'utf-8');
});
//send file
}
else{
console.log ('password wrong');
fs.readFile(__dirname +'/wrongpassword.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
}
}); // req on end function end
}
}).listen(port, host);
If you are willing to use express web framework, then it can be done in a much easier way.
app.get('/download', function(req, res){
var file = __dirname + 'learn_express.mp4';
res.download(file); // Sets disposition, content-type etc. and sends it
});