sendFile absolute path - node.js

On Windows machine, I have a mapped share folder => Z:/uploads/
using express, I use res.senFile to return a file to the browser:
var download = config.file_dir + "/" + file;
res.sendFile(download);
download value is Z:/uploads/737237213791239.pdf
I get this error:
throw new TypeError('path must be absolute or specify root to res.sendFile
Am I giving a absolute path?

I hit the same issue. I found it easiest to configure my folder with forward slashes, the use path.join to send. This should work work a drive letter or UNC path.
var path = require("path");
config.file_dir = "z:/folder";
//use forward slashes for UNC if you wish to use that instead ie //server/share
var file = path.join(config.file_dir, urlPath);
res.sendFile(file, (err) => {
if (err) {
res.status(err.status || 500).send();
}
});

Related

How can I allow a user to download a file from a link in express?

I have a function that allows you to create a folder in the directory where the server is located, and upload files there
And I'm trying to access them via a direct link, like this
http://localhost:5000/attachments/1618413024408--1.jpg
Because, the file is located here
But why can't I access it?
Cannot GET /attachments/1618413024408--1.jpg
Express itself provides an easy to use implementation for this express.static(root, [options]).
Simply add this add the right position in your code:
app.use('/attachments', express.static(path.join(__dirname, 'attachments')))
Make sure that path.join(__dirname, 'attachments') points to the right directory (with a simple console.log) otherwise just adjust it.
Documentation: https://expressjs.com/en/starter/static-files.html
try to use express function sendFile
https://expressjs.com/en/api.html#res.sendFile
something like this
app.use('/attachments/:filename', (req, res) => {
const myIms = <path to directory>
const filePath = myIms + '/attachments/' + req.filename
res.sendFile(filePath, {
headers: {
'Content-Type': 'image/jpg'
}
})
})
or express download function https://expressjs.com/en/api.html#res.download

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 Express sendFile requiring ending slash

I'm using the following get:
app.get("/:project/*", auth, function (req, res) {
Then parsing against a config file to load a directory dynamically:
var path = req.params[0] ? req.params[0] : "index.html";
res.sendfile( path, { root: "./builds/" + config[req.params.project].dir + "/dist/" } );
Which works great, only problem is that it requires my urls to end with a "/" if I'm going to the default (index.html). For example, I have to use http://server.com/some_project/ and if the trailing slash isn't there it throws a Cannot GET /some_project error. Curious if there's a way around this?
app.get("/:project/?*", ...)
Add the question mark. That should allow you to do with or without the trailing slash.

nodejs web root

I was under the impression that when you run a nodejs webserver the root of the web is the folder containing the js file implementing the webserver. So if you have C:\foo\server.js and you run it, then "/" refers to C:\foo and "/index.htm" maps to C:\foo\index.htm
I have a file server.js with a sibling file default.htm, but when I try to load the contents of /default.htm the file is not found. An absolute file path works.
Where is "/" and what controls this?
Working up a repro I simplified it to this:
var fs = require('fs');
var body = fs.readFileSync('/default.htm');
and noticed it's looking for this
Error: ENOENT, no such file or directory 'C:\default.htm'
So "/" maps to C:\
Is there a way to control the mapping of the web root?
I notice that relative paths do work. So
var fs = require('fs');
var body = fs.readFileSync('default.htm');
succeeds.
I believe my confusion arose from the coincidental placement of my original experiment's project files at the root of a drive. This allowed references to /default.htm to resolve correctly; it was only when I moved things into a folder to place them under source control that this issue was revealed.
I will certainly look into express, but you haven't answered my question: is it possible to remap the web root and if so how?
As a matter of interest this is server.js as it currently stands
var http = require('http');
var fs = require('fs');
var sys = require('sys');
var formidable = require('formidable');
var util = require('util');
var URL = require('url');
var QueryString = require('querystring');
var mimeMap = { htm : "text/html", css : "text/css", json : "application/json" };
http.createServer(function (request, response) {
var body, token, value, mimeType;
var url = URL.parse(request.url);
var path = url.pathname;
var params = QueryString.parse(url.query);
console.log(request.method + " " + path);
switch (path) {
case "/getsettings":
try {
mimeType = "application/json";
body = fs.readFileSync("dummy.json"); //mock db
} catch(exception) {
console.log(exception.text);
body = exception;
}
break;
case "/setsettings":
mimeType = "application/json";
body="{}";
console.log(params);
break;
case "/":
path = "default.htm";
default:
try {
mimeType = mimeMap[path.substring(path.lastIndexOf('.') + 1)];
if (mimeType) {
body = fs.readFileSync(path);
} else {
mimeType = "text/html";
body = "<h1>Error</h1><body>Could not resolve mime type from file extension</body>";
}
} catch (exception) {
mimeType = "text/html";
body = "<h1>404 - not found</h1>" + exception.toString();
}
break;
}
response.writeHead(200, {'Content-Type': mimeType});
response.writeHead(200, {'Cache-Control': 'no-cache'});
response.writeHead(200, {'Pragma': 'no-cache'});
response.end(body);
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
I'm not completely certain what you mean by "routes" but I suspect that setsettings and getsettings are the sort of thing you meant, correct me if I'm wrong.
Nodejs does not appear to support arbitrary mapping of the web root.
All that is required is to prepend absolute web paths with a period prior to using them in the file system:
var URL = require('url');
var url = URL.parse(request.url);
var path = url.pathname;
if (path[0] == '/')
path = '.' + path;
While you're correct that the root of the server is the current working directory Node.js won't do a direct pass-through to the files on your file system, that could be a bit of a security risk after all.
Instead you need to provide it with routes that then in turn provide content for the request being made.
A simple server like
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
Will just capture any request and respond in the same way (but doesn't read the file system).
Now if you want to serve out file contents you need to specify some way to read that file into the response stream, this can be done a few ways:
You can use the fs API to find the file on disk, read its contents into memory and then pipe them out to the response. This is a pretty tedious approach, especially when you start getting a larger number of files, but it does allow you very direct control over what's happening in your application
You can use a middleware server like express.js, which IMO is a much better approach to do what you're wanting to do. There's plenty of questions and answers on using Express here on StackOverflow, this is a good example of a static server which is what you talk about
Edit
With the clarification of the question the reason:
var body = fs.readFileSync('/default.htm');
Results in thinking the file is at C:\default.htm is because you're using an absolute path not a relative path. If you had:
var body = fs.readFileSync('./default.htm');
It would then know that you want to operate relative to the current working directory. / is from the root of the partition and ./ is from the current working directory.

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