Serving css, js files in Node.js without frameworks like Express - node.js

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);
}

Related

Why isn't node serving my image file?

I have a vanilla node.js http server. Everything except my image file works. I just get the broken image icon on the page.
Here is my server code:
"use strict";
class app {
constructor() {
app.loadServer();
}
static loadServer() {
const HTTP = require('http'),
PORT = 1337,
SERVER = HTTP.createServer(function(req, res) {
let httpHandler = function(err, str, contentType) {
console.log('\n\n' + 'Content type: ' + contentType + '\n\n');
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('An error has occurred: ' + err.message);
} else if (contentType.indexOf('image') >= 0) {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str, 'binary');
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str);
}
};
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
if (req.method == 'POST') {
app.getFormData(req, res);
} else {
console.log("[405] " + req.method + " to " + req.url);
res.writeHead(405, "Method not supported", { 'Content-Type': 'text/html' });
res.end('<html><head><title>405 - Method not supported</title></head><body><h1>Method not supported.</h1></body></html>');
}
} else if (req.url.indexOf('/javascripts/') >= 0) {
app.render(req.url.slice(1), 'application/ecmascript', httpHandler);
} else if (req.url.indexOf('/css/') >= 0) {
app.render(req.url.slice(1), 'text/css', httpHandler);
} else if (req.url.indexOf('/images/') >= 0) {
app.render(req.url.slice(1), 'image/jpg', httpHandler);
} else {
app.render('public/views/index.html', 'text/html', httpHandler);
}
}).listen(PORT, function() {
console.log('-= Francis Server Listening at http://127.0.0.1:' + PORT + ' =-');
});
}
static render(path, contentType, callback) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
static getFormData(req, res) {
const FORMIDABLE = require('formidable'),
DO_NAMES = require('./node/NameClass');
let formData = {};
new FORMIDABLE.IncomingForm().parse(req)
.on('field', function(field, name) {
formData[field] = name;
})
.on('error', function(err) {
next(err);
})
.on('end', function() {
let finalName = new DO_NAMES(formData);
res.writeHead(200, {'content-type': 'text/plain'});
res.write('-= Received form: ');
res.end(finalName.getFirstName() + ' ' + finalName.getLastName());
});
}
}
module.exports = app;
It feels like it's trying to serve the image as text instead of picture. I verified that the image is there and readable.
I found the problem.
it happens here:
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
You read the image file as UTF-8 but it is a binary file. That is why the image data is corrupt. Instead you have to use binary as encoding.
You could change your code like this:
static render(path, contentType, callback, encoding) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, encoding ? encoding : 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
and then call render like this:
app.render(req.url.slice(1), 'image/jpeg', httpHandler, 'binary');
There are obviously better ways to do it but this requires a minimum amount of change to your code. Just make sure the readFile() encoding is binary for binary files.
Also the correct mime type for jpg is image/jpeg not image/jpg. Most, if not all, browsers won't care but it is more clean.
It looks like your NODE server is setting the wrong MIME type. You can set the MIME type yourself, as you are doing, but this gets awfully painful. I would recommend using a MIME type node module that is made for this exact purpose.
https://www.npmjs.com/package/mime
This npm package does exactly this with very little effort.

How do I incorporate seeking with node express audio stream

I have a small podcast feed that I'm hosting and after I finally got it hooked up to Downcast everything worked fine until I tried to skip forward/back. I finally realized that seeking isn't inherently part of pipe streaming. I've searched around and I cannot seem to find anything of use.
With the following code, how would I implement seeking?
app.get('/stream/:id', function(req, res) {
if (!req.params.id) {
res.status(404);
res.send("Not found");
}
var filePath = path.join(Config.media, '**' , req.params.id);
filePath = glob.sync(filePath)[0];
var stat = fs.statSync(filePath);
res.writeHead(200, {
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size,
'Accept-Ranges': 'bytes'
});
var readStream = fs.createReadStream(filePath);
readStream.pipe(res);
});
I was actually able to do this using visionmedia/send (which handled a lot of my work with paths as well).
Here's the new code that works with seeking:
var send = require('send');
...
app.get('/stream/*', function(req, res) {
var path = Url.parse(req.url).pathname.replace('/stream','');
send(req, path)
.root(Config.media)
.on('error', function(err) {
res.statusCode = err.status || 500;
res.end(err.message);
})
.on('directory', function() {
res.statusCode = 403;
res.end("Forbidden");
})
.pipe(res);
});

NodeJS - Stream video that is being downloaded

I am writing a program that will stream a video file that is currently being downloaded onto the drive. The problem I am having seems to be getting the browser to actually play the video. the script listens for file changes, and then streams the rest, but the browser doesnt do anything besides display a blank Video page.
var fs = require('fs'),
http = require('http'),
filename = '/home/qrpike/Videos/topgears.mp4';
http.createServer(function (req, res) {
console.log(req.url);
if( req.url == '/video.mp4'){
res.writeHead(200,{
'Content-Type' : 'video/mp4',
'Cache-Control' : 'public',
'Connection' : 'keep-alive',
'Content-Disposition' : 'inline; filename=topgears.mp4;',
'Content-Transfer-Encoding' : 'binary',
'Transfer-Encoding' : 'chunked'
});
fs.open(filename, 'r', function(err, fd) {
if (err) throw new Error('Could not open file');
var position = 0;
fs.stat(filename, read);
fs.watchFile(filename, read.bind(null, null));
function read(err, stat) {
var delta = stat.size - position;
if (delta <= 0) return;
fs.read(fd, new Buffer(delta), 0, delta, position, function(err, bytes, buffer) {
console.log("err", err, "bytes", bytes, "position",position,"delta",delta);
res.write(buffer.toString('binary'));
});
position = stat.size;
}
});
}
}).listen(1337);
console.log('Server running at http://127.0.0.1:1337/');
So this answer depends on growing-file, which in theory does what you want. My concern is that the project hasn't had a commit in two years so I have no idea if it still works. That being said, this worked for me locally (though I didn't test piping to the video file):
var fs = require('fs');
var http = require('http');
var GrowingFile = require('growing-file');
var FILENAME = '/home/dave/Desktop/video/video.ogg';
var server = http.createServer(function(req, res) {
var file;
if (req.url === '/video.ogg') {
res.writeHead(200, {
'Content-Type': 'video/ogg'
});
file = GrowingFile.open(FILENAME);
file.pipe(res);
} else {
res.statusCode = 404;
res.end('Not Found');
}
});
server.listen(1337);

Empty HTTP response

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.

Node.js - Socket.io client file not being served by basic node web server

I m very new to Node.js and Socket.io. I have built a very basic web server however when using it, I am unable to load the socket.io client file (I get a 404).
I am trying to use this client side code:
<script src="/socket.io/socket.io.js"></script>
My understanding is that Node should pick this up and resolve it? It does on a much simpler web server example.
My web server example where it is NOT resolved is as follows:
var server = http.createServer(function (request, response) {
console.log('request starting...');
var filePath = '.' + request.url;
if (filePath == './')
filePath = './index.html';
var extname = path.extname(filePath);
var contentType = 'text/html';
switch (extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
}
path.exists(filePath, function(exists) {
if (exists) {
fs.readFile(filePath, function(error, content) {
if (error) {
response.writeHead(500);
response.end();
}
else {
response.writeHead(200, { 'Content-Type': contentType });
response.end(content, 'utf-8');
}
});
}
else {
response.writeHead(404);
response.end();
}
});
My web server code where it DOES resolve is as follows:
app.listen(8080);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
Can anyone advise on what is causing it not serve the socket.io file on the client using the first web server example?
Best regards, Ben.
You are probably running Socket.IO on a different port, so include the file in the following format:
server:port/socket.io/socket.io.js

Resources