What is happening to my file with res.download in Express.js? - node.js

I'm working on an app that creates a PDF document on the server, then displays a Download Here button. When I click the button, the process appears to work. When I inspect the Network>Preview and Network>Headers tabs in the Chrome console I can see that the file has definitely been returned.
The problem is, it does not display and it does not offer the option to save. Am I missing a client side step here? My preferred outcome is either to give the user the option to save the file or to automatically begin the download to their default path.
Here is relevant the server code:
exports.show = function(req, res) {
var file = req.params.id;
var filePath = __dirname + '../../../lib/completedforms/';
var thisPath = path.resolve(filePath + file);
res.attachment(thisPath);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader("Content-Disposition", "attachment");
res.download(thisPath);
};
Thanks in advance for any guidance here.

There's no need for both res.attachment() AND res.download(). Just use the latter.
Also, res.download() already sets the Content-Disposition header, so you can remove that too.
You can also simplify your path generation:
var thisPath = path.resolve(__dirname, '../../../lib/completedforms/', file);
Although you should probably sanitize file and/or check that thisPath is not some location where it shouldn't be. This will prevent someone from supplying a potentially malicious req.params.id value like ../../../../../../../etc/passwd.

Related

Node Express Fast CSV download to client

I've set up a small node js BE app, built with express and fastCsv module on top of it. The desired outcome would be to be able to download a csv file to the client side, without storing it anywhere inside the server, since the data is generated depending on user criteria.
So far I've been able to get somewhere it it, Im using streams, since that csv file could be pretty large depending on the user selection. Im pretty sure something is missing inside the code bellow:
const fs = require('fs');
const fastCsv = require('fast-csv');
.....
(inside api request)
.....
router.get('/', async(req, res) => {
const gatheredData ...
const filename = 'sometest.csv'
res.writeHead(200, {
'Content-Type': 'text/csv',
'Content-Disposition': 'attachment; filename=' + filename
})
const csvDataStream = fastCsv.write(data, {headers: true}).pipe(res)
})
The above code 'works' in some way as it does deliver back the response, but not the actual file, but the contents of the csv file, which I can view in the preview tab as a response. To sum up, Im trying to stream in that data, into a csv and push it to download file to client, and not store it on the server. Any tips or pointers are very much appreciated.
Here's what worked for me after created a CSV file on the server using the fast-csv package. You need to specify the full, absolute directory path where the output CSV file was created:
const csv = require("fast-csv");
const csvDir = "abs/path/to/csv/dir";
const filename = "my-data.csv";
const csvOutput = `${csvDir}/${filename}`;
console.log(`csvOutput: ${csvOutput}`); // full path
/*
CREATE YOUR csvOutput FILE USING 'fast-csv' HERE
*/
res.type("text/csv");
res.header("Content-Disposition", `attachment; filename="${filename}"`);
res.header("Content-Type", "text/csv");
res.sendFile(filename, { root: csvDir });
You need to make sure to change the response content-type and headers to "text/csv", and try enclosing the filename=... part in double-quotes, like in the above example.

Using directory (for images links etc) in Openshift (nodejs application)

I have a webpage that I have hosted using a node application on openshift. Its here
http://nodejs-volition.rhcloud.com/
My question is very simple (although I haven't found anyone else asking it). How do I refer to other files in the directory which contains index.html
For instance I would like to use an image that is in the directory in the index. My current html for the image is
<img src="$OPENSHIFT_REPO_DIR/images/1416870991752.jpg" alt="spark core">
I have also tried using "images/1416870991752.jpg". I have the same problem with linking to other html files in the directory?
What am I doing wrong? Please help?
As corey112358 alludes to below the key is in that to host using nodejs a server must be defined. My application already has a server file, so rather than creating a new server I must modify the existing one. I've done it successfully now, there were two changes to make to the server.js file.
The 1st change is modification of the cache. That should look like this...
self.zcache['index.html'] = fs.readFileSync('./index.html');
self.zcache['page2.html'] = fs.readFileSync('./page2.html');
self.zcache['sparkcoredark.jpg'] = fs.readFileSync('./sparkcoredark.jpg');
The first line was already included but the next two were added by me to include another html page and an image.
The second step is modify the self.createRoutes section of the server.js file as below (asciimo image is included by default).
self.createRoutes = function() {
self.routes = { };
self.routes['/asciimo'] = function(req, res) {
var link = "http://i.imgur.com/kmbjB.png";
res.send("<html><body><img src='" + link + "'></body></html>");
};
self.routes['/'] = function(req, res) {
res.setHeader('Content-Type', 'text/html');
res.send(self.cache_get('index.html') );
};
self.routes['/page2.html'] = function(req, res) {
res.setHeader('Content-Type', 'text/html');
res.send(self.cache_get('page2.html') );
};
self.routes['/sparkcoredark.jpg'] = function(req, res) {
res.setHeader('Content-Type', 'image/jpg');
res.send(self.cache_get('sparkcoredark.jpg') );
};
};
Hope that helps out anyone else struggling with this issue. Thanks to coreyfibonacci

Expressjs file download - not downloading

I can't seem to get my "download file" feature working using Expressjs.
//DOWNLOAD FILE
router.get('/snippets/download', function (req, res) {
res.attachment("untitled.js");
res.send("here is some javascript");
});
If I access this route in my browser the file downloads to my computer but not if I use an Angularjs request to the route.
Am I missing something?
You can use res.download. Refer documentation here: http://expressjs.com/4x/api.html
Eg:
//DOWNLOAD FILE
router.post('/snippets/download', function (req, res) {
res.download(req.body.filename, req.body.text);
});
See if this helps.
The res.download() method need a file's full path ( which could be different in windows and linux with different seperator ).
And the 2nd param of res.download(localName, downloadPromptName ) should be able to modify the filename that user see (different from the file in your server's directory), but it seems that does not work in my environment.
So I recommend you to use res.sendFile(fullNameInServer ,options) where you can modify the downloaded filename in options.
var root = getDownloadRoot(req);
var options = {
root: getDownloadRoot(req),
headers: {
"content": "text/html;charset=utf-8",
"Content-Type": "application/octet-stream",
"Expires":"0",
"Cache-Control": "must-revalidate, post-check=0, pre-check=0",
"content-disposition": "attachment;filename=" + urlencode(downloadFilename)
}
};
res.sendFile( tempFileName ,options);
urlencode should be required to encode filename then you can use filename other than english.
before call download file , you need to write the file physically in a temp folder,
the getDownloadRoot() method give you the temp folder location in runtime which does not vary when you change the path to run the app.
here is the function getDownloadRoot()
function getDownloadRoot(req){
var path = require('path');
var sep = path.sep;
var parentPath = path.dirname(req.settings.views);
var ret = parentPath.concat(sep + tempFileFolder);
return ret;
}
For now I have no way other than using app.setting (that app is declared in app.js) to get the application folder during runtime. So I made a litte 'middleware' to transport the value with req object as following.
In app.js:
app.use(function(req, res, next) {
req.settings = app.settings;
next();
});
tempFileFolder is a folder that you can name it yourself.
sep is the folder seperator ( \ in windows and / in linux )
Also you need to watch the folder permission settings when running in linux.
This combination works perfectly in my environment (with angularjs)

Node.js check if multiple files exists with unspecific extension

I have using node.js to develop a application that grabs an user's avatar file saved I the file system.
var fs = require('fs');
fs.exists(__dirname + "/../public/uploaded/users/" + user.username + "/avatar.png", function(exists){
if(exists){
//...
} else {
//...
}
}
But this checks only if avatar.png exists. Some avatar file such as avatar.gif or avatar.jpg could also be grabbed.
I know I can put three level of if statements inside if(exists) but I want to know a better way.
Thanks,
Dennis
I'm not sure if there is an easy native method but this library seems like it would do what you are looking for: https://github.com/isaacs/node-glob

How to link files in NodeJS (+ let the user download the file when clicking it)?

I guess it is just a simple question but I can't solve it on my own.
(I'm using Express + NodeJS)
What I would like to have is a directory listing with the files contained in it. The files shall be linked so that a user could download them by just clicking the link (like the standard directory listing you get if you have e.g. a apache server without any index file).
To list the directory content I use
var fs = require('fs');
fs.readdir('./anydir', function(err, files){
files.forEach(function(file){
res.send(file);
});
});
(Notice: I did not include any error handling in this example as you can see)
Now I tried to just link the file by modifying the
res.send(file)
to
res.send('<a href=\"' + file + '\">' + file + '<br>');
but this just prints out the error message:
Cannot GET /anydir/File
... because I did not handle every file request in app.js.
How can I achieve my goal mentioned above?
Just use express.directory and express.static as middleware, possibly with a user-defined middleware to set Content-Disposition headers.
This worked for me:
var fs = require('fs');
fs.readdir('/var/', function(err, files){
files.forEach(function(file){
res.write('<a href=\"' + file + '\">' + file + '<br>');
});
});
You put the content with write() not send().
Try this and let me know. I can see the list of files displayed correctly.
Hope this helps you.

Resources