I am trying to display a html file in browser using Nodejs. But when I run the code I got the following error:
cannot read property isFile() of undefined
This is the code that I am using:
var http = require('http');
var url = require('url');
var path = require('path');
var fs = require('fs');
var mimeTypes = {
"html" : "text/html",
"jpeg" : "image/jpeg",
"jpg" : "image/jpg",
"png" : "image/png",
"js" : "text/javascript",
"css" : "text/css"
};
var stats;
http.createServer(function(req, res) {
var uri = url.parse(req.url).pathname;
var fileName = path.join(process.cwd(),unescape(uri));
console.log('Loading ' + uri);
try {
stats = fs.lstat(fileName);
} catch(e) {
res.writeHead(404, {'Content-type':'text/plain'});
res.write('404 Not Found\n');
res.end();
return;
}
// Check if file/directory
if (stats.isFile()) {
var mimeType = mimeTypes[path.extname(fileName).split(".").reverse()[0]];
res.writeHead(200, {'Content-type' : mimeType});
var fileStream = fs.createReadStream(fileName);
fileStream.pipe(res);
return;
} else if (stats.isDirectory()) {
res.writeHead(302, {
'Location' : 'index.html'
});
res.end();
} else {
res.writeHead(500, {
'Content-type' : 'text/plain'
});
res.write('500 Internal Error\n');
res.end();
}
}).listen(3000);
The error I am getting is near stats.isFile(). I tried to resolve the error. But it is not working for me. I need some suggestions on resolving this error.
The variable stats gets set to undefined, without throwing an error. This happens because fs.lstat(fileName) returns undefined.
Before the if statements, or perhaps instead of the try catch block, you may want to do something like:
if (!stats) {
res.writeHead(404, {'Content-type':'text/plain'});
res.write('404 Not Found\n');
res.end();
return;
}
You are using wrong function. You should use:
stat=fs.lstatSync("your file")
Then your code should work.
fs.lstat("your file",function (err,stats){})
is an async function which expects callback. Take a look at the documentation here.
Related
I'm trying to get - after some promises have been executed - a CSV result back together with a status reponse having details.
The response does get me the data for the CSV but I cannot seem to get the browser to download this data into a CSV file.
router.post('/getSalesOrders', function (request, response) {
var data = request.body;
salesOrderActions.retrieveSalesOrders(data) //
.then(function (result) {
response.setHeader('Content-disposition', 'attachment; filename=testing.csv');
response.set('Content-Type', 'text/csv');
response.json(result[0].message).send(result[0].file);
})
.catch(function (err) {
console.log(err);
if (err.statusCode) {
response.json(err);
}
else {
var error = output.getCriticalErrorResult(c.titles.SERVICE_CRITICAL_ERROR, c.messages.UNKNOWN_ERROR, err.message);
response.json(error);
}
});
});
My result object gets created in the salesOrderActions:
I am here using npm package json2csv
var fields = ['id',.....];
var csv = csvParser({ data: unmatchedLines, fields: fields });
return {
file: csv,
message:
output.getSuccessResult(
titles.SALES_ORDER_SUCCESS_RETRIEVAL,
salesDataForModel.identifier
)
}
My response to the browser is as follows:
So my message isn't sent it seems and I do get the CSV data but not as a file to download.
How can I manage that?
As a sidenote maybe, my front-end is React
EDIT
Response with octed headers:
Try:
sending Content-Type before Content-Disposition
quoting the filename: filename="testing.csv"
Also HTTP headers are case insensitive, so it should not make a difference, but you should write Content-Disposition (capital D).
response.set('Content-Type', 'text/csv');
response.setHeader('Content-Disposition', 'attachment; filename="testing.csv"');
If this does not work you can change the Content-Type to application/octet-stream
This always forces the browser to download the data sent from the server.
Try this code:
router.post('/getSalesOrders', function (request, response) {
var data = request.body;
var fs = require('fs');
salesOrderActions.retrieveSalesOrders(data) //
.then(function (result) {
//**********
var file = "testing.csv";
response.setHeader('Content-disposition', 'attachment; filename=testing.csv');
response.set('Content-Type', 'text/csv');
var filestream = fs.createReadStream(file);
filestream.pipe(res);
//*********
})
.catch(function (err) {
console.log(err);
if (err.statusCode) {
response.json(err);
}
else {
var error = output.getCriticalErrorResult(c.titles.SERVICE_CRITICAL_ERROR, c.messages.UNKNOWN_ERROR, err.message);
response.json(error);
}
});
});
So actually it turns out it is because I'm doing an Ajax request which doesn't - by default - prompt the browser to download any files.
What I did in the end:
router.post('/getSalesOrders', function (request, response) {
var data = request.body;
salesOrderActions.retrieveSalesOrders(data)
.then(function (result) {
response.json(result);
})
.catch(function (err) {
//...
});
});
And then in my front-end, when receiving the result:
salesOrderService.retrieveSalesOrderData()
.then(function (result) {
self.convertAndDownloadCsv(result.unmatchedLines);
});
convertAndDownloadCsv: function (data) {
if (data && data.length > 0) {
var csvData = csvProcessor({ //using csv2json node js package
data: data,
quotes: '',
del: ';'
});
var filename = "unmatchedLinesFromSalesOrders.csv";
var blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, filename);
} else {
var link = document.createElement("a");
if (link.download !== undefined) { // feature detection
// Browsers that support HTML5 download attribute
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
}
More info can be found here
I am very new to express and node. I was trying to upload an image using multiparty and code given here.
I have put a check for file size. When I upload a file of size greater than the limit it lands in the "problem section". The problem is the server hangs and responds back only after request timeout. I have tried many solution on stack overflow but nothing seems to work. It works if the file size is below the limit. I am very sure that the code reaches the problem section and there is no problem with the upload logic. But it seems that I have to do something in the "problem section". Please tell me what am I missing.
I have replaced the code in the problem section with
next(), res.send(), res.end(), next(err), return; but It does not work. It hangs no matter what.
Following is the code:
router.post("/image", function(req, res, next) {
if(req.user) {
upload.uploadToS3(req, S3_BUCKET, S3_PROFILE_IMAGE_FOLDER, function(result) {
if(result.error != null && result.error === false) {
models.Customer.update({
orignalImage : result.fileLocation
},{
where : { mobileNumber : req.user.mobileNumber}
}).then(function(customer) {
if(customer) {
res.send({
url: result.fileLocation,
error : false
});
} else {
res.status(400);
res.send({error : true,
error_message : 'Image upload failed'});
}
});
} else {
//PROBLEM SECTION
res.status(404);
res.json({error : true, error_message : result.error_message});
}
});
} else {
res.status(403);
res.send({message: "access denied"});
}
});
response after timeout
Please tell me if you need more details I will upload it.
var uploadToS3 = function(req, S3_BUCKET, folder, callback) {
var s3Client = knox.createClient({
secure: false,
key: awsConfig.accessKeyId,
secret: awsConfig.secretAccessKey,
bucket: S3_BUCKET,
});
var headers = {
'x-amz-acl': 'public-read',
};
var form = new multiparty.Form();
var batch = new Batch();
batch.push(function(cb) {
form.on('part', function(part) {
var validity = validateFile({type : part.headers['content-type'], name : part.filename, length : part.byteCount});
console.log(validity);
if(validity.isValid) {
cb(null, { filename : folder+"/"+generateFileName({name : part.filename}), part : part});
} else {
cb({error : true, error_message : validity.reason, part:part }, "advra kedavra");
}
});
});
batch.end(function(err, results) {
if (err) {
console.log(err);
err.statusCode = 200;
callback(err);
} else {
form.removeListener('close', onEnd);
var result = results[0];
var part = result.part;
var destPath = result.filename;
var counter = new ByteCounter();
part.pipe(counter); // need this until knox upgrades to streams2
headers['Content-Length'] = part.byteCount;
s3Client.putStream(part, destPath, headers, function(err, s3Response) {
result = {};
if(err) {
console.log(err);
result.error = true;
result.error_message = "Problem in uploading!";
} else {
console.log(s3Response.req.url);
result = {error: false, statusCode : s3Response.statusCode, message : "file upload successful.", fileLocation : s3Response.req.url};
}
callback(result);
});
part.on('end', function() {
console.log("File upload complete", counter.bytes);
});
}
});
function onEnd() {
console.log("no uploaded file");
callback({error:false, error_message:"no uploaded file."});
}
form.on('error', function(err) {
console.log('Error parsing form: ' + err.stack);
});
form.on('close', onEnd);
form.parse(req);
}
After a 3 day long search for the answer I found one answer. Express.js close response
The problem section should be the following :
res.status(400);
res.set("Connection", "close");
res.json({error:true, error_message : "<some - error message>"});
You should simply add a .end() after setting the status as: res.status(400).end();
See official docs
res.end([data] [, encoding])
Ends the response process. This method actually comes from Node core, specifically the response.end() method of http.ServerResponse.
Use to quickly end the response without any data. If you need to respond with data, instead use methods such as res.send() and res.json().
res.end();
res.status(404).end();
res.status(400);
res.set("Connection", "close");
res.json({error:true, error_message : "<some - error message>"});
I am not sure that solves your issue. The 'problem section' is in your callback, which would only run after the upLoadToS3 function runs. The 'problem' is probably with that function. You might have to refactor that to handle large file uploads.
This question has probably been asked earlier but the answers are not very convincing.
I have the following code with me -
var http = require('http'),
fs = require('fs');
connect = require('connect');
function serveStaticFile(res, path, contentType, responseCode) {
if(!responseCode) responseCode = 200;
fs.readFile(__dirname + path, function(err,data) {
if(err) {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('500 - Internal Error');
} else {
res.writeHeader(responseCode,
{ 'Content-Type': contentType });
res.write(data)
res.end();
}
});
}
http.createServer(function(req,res){
// normalize url by removing querystring, optional
// trailing slash, and making lowercase
var path = req.url.replace(/\/?(?:\?.*)?$/, '')
.toLowerCase();
switch(path) {
case '': serveStaticFile(res, '/public/index.html','texthtml');
break;
default:
serveStaticFile(res, '/public/404.html', 'text/html',
404);
break;
}
}).listen(3000);
console.log('Server started on localhost:3000; press Ctrl-C to terminate....');
I am just not able to load the css and js files mentioned in the index.js. What am I doing wrong? If I open index.js in the browser directly, the css and js load fine.
Express uses serve-static for serving static directories which is uses send for streaming files. You can use that instead of express, or take at a look their implementation.
What are you think about send ? Use that like this:
function serveStaticFile(res, path, contentType, responseCode) {
if(!responseCode) responseCode = 200;
res.status = responseCode;
function error(err) {
res.statusCode = err.status || 500;
res.end(err.message);
}
function headers(res, path, stat) {
res.setHeader('Content-type', contentType);
}
send(req, path, {root: __dirname})
.on('error', error)
.on('headers', headers)
.pipe(res);
}
I'm trying to upload a video to vimeo with nodejs (https://developer.vimeo.com/apis/advanced/upload at step 3) . This is what I currently do:
Firstly I call the function to read the file:
var options = {
hostname : dataObject.ticket.host,
path : '/upload?ticket_id=' + dataObject.ticket.id,
port : 8080,
method: 'POST'
}
postMovie(options);
I get these parameters from my object:
{
"generated_in": "0.0308",
"stat": "ok",
"ticket": {
"endpoint": "http://126535.cloud.vimeo.com:8080/upload?ticket_id=9d818e8bd066dfd54e53f1be2fa3f958",
"endpoint_secure": "https://126535.cloud.vimeo.com/upload?ticket_id=9d818e8bd066dfd54e53f1be2fa3f958",
"host": "126535.cloud.vimeo.com",
"id": "9d818e8bd066dfd54e53f1be2fa3f958",
"max_file_size": "26843545600"
}
}
This function is called :
function postMovie(options){
// This is an async file read
fs.readFile('public/uploads/4363066343.mp4', function (err, data) {
if (err) {
console.log("FATAL An error occurred trying to read in the file: " + err);
process.exit(-2);
}
// Make sure there's data before we post it
if(data) {
PostData(data,options);
}
else {
console.log("No data to post");
process.exit(-1);
}
});
};
When the file is read:
function PostData(data,options) {
var headers = {
'Content-Type': 'video/mp4',
'Content-Length': data.length
}
options.headers = headers
console.log(options)
// Set up the request
var post_req = http.request(options, function(res) {
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
// post the data
post_req.write(data);
post_req.end();
post_req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
}
My post_req.on(error) logs this:
problem with request: write EPIPE
problem with request: write EPIPE
I understand this is because of a time-out at the serverside.
I assume my request is not well formed.
Can someone point out what I did wrong ?
The upload operation will be much simpler with the request module.
var inspect = require('eyespect').inspector();
var request = require('request')
var path = require('path')
var fs = require('fs')
var filePath = path.join(__dirname, '../public/uploads/foo.mp4')
fs.stat(filePath, function(err, stats) {
if (err) {
inspect(err, 'error stating file')
return
}
var fileSize = stats.size
var url = 'https://126535.cloud.vimeo.com/upload?ticket_id=9d818e8bd066dfd54e53f1be2fa3f958'
var opts = {
url: url,
method: 'post',
headers: {
'Content-Length': fileSize,
'Content-Type': 'foo'
}
var r = request(opts)
// pipe the file on disk to vimeo
var readStream = fs.createReadStream(filePath)
readStream.pipe(r)
readStream.on('error', function (err) {
inspect(err, 'error uploading file')
})
readStream.on('end', function (err) {
inspect('file uploaded correctly')
})
})
Request also allows you to set the timeout option as well if the file is big and thus takes a long time to upload
For some reason, this code keeps failing :
var http = require('http');
var url = require ('url');
var jade = require('jade');
var fs = require('fs');
http.createServer(function(req, res) {
// Request handlers
var path = url.parse(req.url, true).pathname;
var dot = path.lastIndexOf('.');
var extension = path.substr(dot + 1);
// Response handlers
switch(extension) {
case 'css' :
res.writeHeader(200, {"Content-Type": "text/css"});
fs.readFile(path.substr(1, dot - 1) + '.css', function (err, css) {
if(err) {
res.write(err, 'utf8')
};
res.write(css, 'utf8')
});
break;
case 'js' :
res.writeHeader(200, {"Content-Type": "text/javascript"});
fs.readFile(path.substr(1, dot - 1) + '.js', function (err, js) {
if(err) {
res.write(err, 'utf8')
};
res.write(js, 'utf8')
});
break;
default :
res.writeHeader(200, {"Content-Type": "text/html"});
jade.renderFile(path.substr(1, path.length) + '.jade', { title: 'test' }, function(err, html) {
if(err) {
res.write(err, 'utf8')
};
res.write(html, 'utf8');
});
break;
}
res.end();
}).listen(8080);
I don't get any errors, it's just that the responses seem to be empty.
There is a header but no css or javascript file.
The way I link them in Jade is the following :
link(rel='stylesheet', href='/css/bootstrap.min.css')
link(rel='stylesheet', href='/css/style.css')
Thank you in advance !
You've called res.end() too soon. Instead, put it after the last res.write() in each of your fs.readFile handlers.