I am using Express 4.13, and I met some problems when I tried to serve the files:
function doServeFile(name, fullpath, res, download) {
if (fullpath) {
if (download)
res.download(fullpath, name);
else {
res.sendFile(fullpath, {}, function (err) {
console.info(err);
});
}
}
else {
res.status(404);
}
}
function serveFile(id, res, download) {
Model.findById(id).then(file=> {
var fullPath = Config.uploadDest + "/" + file.path;
doServeFile(file.filename, fullPath, res, download)
}).catch(Util.dbErrorHandler);
}
router.get("/:id", function (req, res, next) {
serveFile(req.params.id, res);
});
router.get("/download/:id", function (req, res, next) {
serveFile(req.params.id, res, true);
});
As the codes shown, once I send request /1 to the server, it will retrieve the file whose id is 1 to get the file path, and then use the res.sendFile to serve the file to client.
However when I run the application, I found that the request will hang on too long to have the response. But I can see the logs like this:
---try send file:D:/file_upload/1464578330791_user.jpg
It seems that the file has been fetched, but the res.sendFile does not complete its job.
Also, when I tried /download/1, the file can be downloaded.
What's going on ?
I had this same issue the other day and found that sendFile doesn't actually acknowledge absolute paths correctly and hangs. Setting the root option solved the issue. Hopefully it might work for you.
res.sendFile(fullpath, {root: '/'});
After dig into the source codes of express, I found the answer.
When the res.sendFile is called, express will to make sure that the root of the path is set or the path must be an absolute path, check this, it is just the isAbsolutepath which cause this problem,codes:
exports.isAbsolute = function(path){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
So a path like
D:/a/a.txt will not be treated as an absolute path!
D:\\a\\a.txt will be. And so does linux-like path /home/a/a.txt.
And this is how I build the path(manually), which is not an absolute path from express's perspective:
var fullPath = Config.uploadDest + "/" + file.path;
So I change it to:
var fullPath = path.join(Config.uploadDest,file.path);
It worked.
If you are using windows operating system, use its full path such as
D:/folder1/folder2/index.html
Related
I am trying to create a redirect after a get request to a static image asset using something like this but I can't figure it out.
Can anyone give me an idea?
app.get('/pixel-tracker.gif', function(req, res, next) {
var options = {
root: path.join(__dirname, 'public'),
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
}
var fileName = req.params.name;
res.sendFile(fileName, options, function (err) {
if (err) {
next(err)
} else {
console.log('Sent:', fileName);
res.redirect('/pixel-parser');
}
});
})
Calling res.redirect after res.sendFile doesn't make sense. By calling res.sendFile you're responding with the file, i.e. you've already responded to the client and try to send another redirect response. You need to either send a file or redirect, can't do both.
I figured this out.
You can't set an app.post to fire on a route where you have a public asset. I removed the gif from the public folder and it works perfectly now.
I'm allowing users to upload files and would like to know if it is possible to only allow the user who uploaded their files access to it.
Currently I am just uploading these to a static folder (Public).
Simply put a route to render the file (this will decrease performance)
Save the file as : filename.extension.userid in a non public folder for example a directory named upload.
Put a route to catch the request on the directory upload :
app.get("/upload/:filename", function(req, res){
var fname = req.params.filename;
var userid = req.user.id; // RETRIEVE AUTHENTICATED USER ID
var fullname = fname+"."+userid;
fs.readFile(__dirname+"/../public/upload/"+fullname, 'base64', function(err, data){
if(err || !data) return res.status(404);
res.set('Content-Disposition', 'attachment; filename="'+fname+'"');
res.set('Content-Type', 'application/pdf'); // example for pdf
return res.end(data, 'base64');
});
});
I couldn't find a reason why you were suggesting using another route to handle the upload. I was able to post to the same route. All that I needed to do was include the path and set it accordingly.
var folder = newPath = path.join(__dirname, '..', './uploads')
var newPath = folder + '/' + callback.fileName;
fs.writeFile(newPath, data, function(err) {
if (err) {
console.log(err)
} else {
//Do stuff
}
});
After the file was uploaded I was then able to complete my task.
I am fairly new to Node.js, and I am using Express and Busboy-Connect to create a simple file upload form, for wav files only.
Here is what I am trying to do :
- start the upload
- if the mimetype is not wav, redirect to an error page
- else : write the file on the server and redirect back.
If the mimetype is valid, everything works fine, but if it isn't I cannot redirect and the browser is just hanging and eventually times out.
My understanding of it is that the browser doesn't want to redirect because it is waiting for the upload to finish, but how can I cancel the upload then within my js code ?
I could work around the issue and write the file then delete it if it's not the right mimetype, but I think it's a bit stupid to do that, I'd rather find a way to trigger an event that will stop it and redirect immediately.
Here is (a snippet of) my app code :
app.get('/', function (req, res) {
res.render(__dirname + '/public/index.ejs', {error: 0});
});
app.get('/error', function (req, res) {
res.render(__dirname + '/public/index.ejs', {error: 1});
});
app.post('/upload', function (req, res) {
var timestamp = new Date().getTime().toString();
//console.log(timestamp);
var fstream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
if ("audio/wav" != mimetype)
{
console.log("invalid mimetype"); // that prints ok
// req.busboy.end(); // I tried that but it doesn't work
res.redirect('/error');
}
else
{
console.log("Uploading: " + mimetype);
fstream = fs.createWriteStream(__dirname + '/tmp/' + timestamp + filename);
file.pipe(fstream);
fstream.on('close', function () {
res.redirect('back');
});
}
});
});
Can anyone point me in the right direction?
Thank you for your help !
Alright I found it in the docs of npm, if you think anyone could be interested in finding this answer from a google search you can leave it resolved, otherwise feel free to close/remove this post.
Basically there is a function on the filestream that need to be called to unblock busboy, so all I had to do to make it work is to add
file.resume();
before redirecting to the error page.
I'm working on creating a simple file uploader on a node server with expressjs as the middleware. So far, the server side looks like:
app.post('/upload', function(req, res) {
console.log(req.files);
//Handle the file
fs.readFile(req.files.imageUploader.path, function(err, data) {
var newPath = __dirname;
console.log(newPath);
console.log(data);
fs.writeFile(newPath, data, function(err) {
console.log(err);
res.send("AOK");
});
});
});
Now, the log statement for __dirname is my source directory, as expected (C:\Development\GitHub\ExpressFileUpload), however an error is happening on the upload:
{ [Error: EISDIR, open 'C:\Development\GitHub\ExpressFileUpload']
errno: 28,
code: 'EISDIR',
path: 'C:\\Development\\GitHub\\ExpressFileUpload' }
I've tried changing the newPath to be / and ./ but no change, different errors, but still errors. Is it something to do with the double \\ in the path in the error? Am I missing something simple here? Thanks for the help, let me know if more info is needed.
The __dirname global object is a directory, not a file. Therefore, you can't open it for writing, which is what fs.writeFile() attempts to do in your script, hence the reason you are getting a EISDIR. Assuming you want the file to be written with the same name it was uploaded with, you can do this:
var file = req.files.imageUploader;
fs.readFile(file.path, function(err, data) {
var path = __dirname + '/' + file.name;
fs.writeFile(path, data, function(err) {
});
});
I am building a NodeJs SOAP client. Originally, I imagined the server (ie the node SOAP client) would allow downloading documents through a REST API (the REST API is authenticated). After a good deal of time on Google and SO, looks like that is not possible.
That means when a document download is requested, I'll have to make a SOAP call for the document and return a URL to the REST client via AJAX.
In order to do this I'll need to:
Temporarily create a file in Node
get its URL and return to web client
When the file is requested and response is sent, delete the file (for security purposes)
Here are my questions:
Is there already a framework that does this? the temp module might be an option, but really I'd like to delete after every request, not after a time period.
If not, can I do this just using the NodeJs File System, and Express static module? Basically we would modify the static module to look like this:
return function static(req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) return next();
var path = parse(req).pathname;
var pause = utils.pause(req);
/* My Added Code Here */
res.on('end', function(){
// delete file based on req URL
})
/* end additions */
function resume() {
next();
pause.resume();
}
function directory() {
if (!redirect) return resume();
var pathname = url.parse(req.originalUrl).pathname;
res.statusCode = 301;
res.setHeader('Location', pathname + '/');
res.end('Redirecting to ' + utils.escape(pathname) + '/');
}
function error(err) {
if (404 == err.status) return resume();
next(err);
}
send(req, path)
.maxage(options.maxAge || 0)
.root(root)
.hidden(options.hidden)
.on('error', error)
.on('directory', directory)
.pipe(res);
};
Is res.on('end',... vaild? Alternatively,should I create some middleware that does this for URLs pointing to the temporary files?
Found two SO questions that answer my question. So apparently we don't need to use the express.static middleware. We just need the filesystem to download a file:
app.get('/download', function(req, res){
var file = __dirname + '/upload-folder/dramaticpenguin.MOV';
res.download(file); // Set disposition and send it.
});
If we want to stream and then delete follow:
app.get('/download', function(req, res){
var stream = fs.createReadStream('<filepath>/example.pdf', {bufferSize: 64 * 1024})
stream.pipe(res);
var had_error = false;
stream.on('error', function(err){
had_error = true;
});
stream.on('close', function(){
if (!had_error) fs.unlink('<filepath>/example.pdf');
});
If you are visiting this SO page after Dec 2015, you may find that the previous solution isn't working (At least it isn't working for me). I found a different solution so I thought I would provide it here for future readers.
app.get('/download', function(req, res){
res.download(pathToFile, 'fileNameForEndUser.pdf', function(err) {
if (!err) {
fs.unlink(path);
}
});
});