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

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.

Related

Unable to use res.send and res.download in Node/Express due to headers already being set

I am new to Node, and I am trying to make it so that when I go to 'localhost:1337/download/open' it renders a webpage, as well as download a file.. I understand that you can only set a header once (that is the error I am getting), but what is the easiest way to both render html AND download a file? Code below:
const express = require('express');
const app = express();
app.get('/download/open', function (req, res) {
let file = `${__dirname}/downloads/Open Tasks.csv`;
res.download(file);
res.send("words");
})
app.listen(1337, function (err) {
if (err) {
console.log(err)
return
}
console.log(`App running. listening on: http://localhost:1337`);
});
Error:
Error: Can't set headers after they are sent.
Thank you in advance.
I was able to figure out what I was trying to do. Instead of trying to render a whole new page AND download a file, I needed to dedicate a route to just a download through the use of an <a></a> tag.
For instance, if I have a webpage at 'http://localhost:1337' that has a link on it like:
Download Open Tasks
Download Open Tasks
Then in node.js I have a route for 'download/open' like so:
app.get('/download/open', function (req, res) {
let file = `${__dirname}/downloads/Open Tasks.csv`;
res.download(file);
})
It will not open a new page (like I thought it needed to) it will just download the file.
IMO, I would suggest you should do the following to achieve your goal:
render the HTML result for "GET http://localhost:1337/download/open"
In the HTML file /download/open, put AJAX block to invoke download file operation
(Download a file by jQuery.Ajax)
$(document).ready(function(){
//code to invoke download file....
});

What is happening to my file with res.download in Express.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.

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)

How to create project in node.js using html5 and mysql?

I want to include my another js file and one html file(dashboard.html).How it will possible?
var file = require('display.js'),
var http = require('http'),
fs = require('fs');
fs.readFile('./dashboard.html',file, function (err, html) {
if (err) {
throw err;
}
http.createServer(function(request, response) {
response.writeHeader(200, {"Content-Type": "text/html"});
response.write(html);
response.end();
}).listen(9236);
});
Please suggest me proper way to do this thing?
In nodejs if you want to add your own libraries/js.
use this
var file = require('./display.js'), // or the path to file it will load file from path
instead of
var file = require('display.js'), // here it will search in node_modules filder
You should know how node look for modules. I'm adding what I know.
If you will require some ./somefolder/somefile.js it will search for the path ./somefolder/somefile.js and load that file.
If you will add 'somefile.js it will search for the librery somefile.js to node_modules folder in your root directory. If it won't get the file there it will go to global path of node_modules.
If it didn't get that file in global node_modules also, it throws an error
Cannot find module <file.js>
Anyone can improve my answer if anything I missed.

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

Resources