I am not able to use more HTML files. I want to add 2-3 HTML links in my index.html, how can I do it? Below is my code where I used 2 html pagelinks, one css and one js.
This is test.js file:
var http = require('http'),
fs = require('fs');
http.createServer(function (req, res) {
if(req.url.indexOf('.html') != -1){ //req.url has the pathname, check if it conatins '.html'
fs.readFile(__dirname + '/public/index.html', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.js') != -1){ //req.url has the pathname, check if it conatins '.js'
fs.readFile(__dirname + '/public/js/bootstrap.min.js', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/javascript'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.css') != -1){ //req.url has the pathname, check if it conatins '.css'
fs.readFile(__dirname + '/public/css/bootstrap.min.css', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/css'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.html') != -1){ //req.url has the pathname, check if it conatins '.js'
fs.readFile(__dirname + '/public/hello.html', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
});
}
}).listen(3000, '127.0.0.1');
console.log('Server running at http://127.0.0.1:3000/');
I got this error in cmd while loading server:
D:\Nodejs>node test.js
Server running at http://127.0.0.1:3000/
events.js:160
throw er; // Unhandled 'error' event
^
Error: write after end
at ServerResponse.OutgoingMessage.write (_http_outgoing.js:441:15)
at D:\Nodejs\test.js:45:13
at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:446:3)
It seems that you are trying to write your own static file server in Node. There are easy solution for that, like the express.static middleware in the Express framework, where all you do is something like this:
Ready solution
var path = require('path');
var express = require('express');
var app = express();
var dir = path.join(__dirname, 'public');
app.use(express.static(dir));
app.listen(3000, () => console.log('Listening on http://localhost:3000/'));
(this is the entire program).
Reinventing the wheel
Now, if you insist of reimplementing all of that yourself then read on.
One of the bugs in your code is checking for the extensions when you in fact check for having strings like .html anywhere in the code. E.g. this:
if(req.url.indexOf('.html') != -1)
will match files like /my.html.styles/style.css which it shouldn't.
If you want to check the file extension then use:
path.extname(file);
See: https://nodejs.org/api/path.html
Also it doesn't terminate when it finds a match because you're using plain if and not else if blocks and you don't use early return in your code.
Another problem is that you have hardcoded all of the paths to files and with that code you will not be able to serve just any HTML or CSS code. Also your code will not serve images correctly, or multiple styles etc. It would actually be easier to rewrite from scratch that try to fix it.
Good examples
See this answer for examples of how to properly serve static images with Express andexpress.static, Express without express.static, connect, http module (like you do here) and net module with raw TCP connections:
How to serve an image using nodejs
Here is a full example of a file server using only http i.e. what you're trying to do here:
var path = require('path');
var http = require('http');
var fs = require('fs');
var dir = path.join(__dirname, 'public');
var mime = {
html: 'text/html',
txt: 'text/plain',
css: 'text/css',
gif: 'image/gif',
jpg: 'image/jpeg',
png: 'image/png',
svg: 'image/svg+xml',
js: 'application/javascript'
};
var server = http.createServer((req, res) => {
var reqpath = req.url.toString().split('?')[0];
if (req.method !== 'GET') {
res.statusCode = 501;
res.setHeader('Content-Type', 'text/plain');
return res.end('Method not implemented');
}
var file = path.join(dir, reqpath.replace(/\/$/, '/index.html'));
if (file.indexOf(dir + path.sep) !== 0) {
res.statusCode = 403;
res.setHeader('Content-Type', 'text/plain');
return res.end('Forbidden');
}
var type = mime[path.extname(file).slice(1)] || 'text/plain';
var s = fs.createReadStream(file);
s.on('open', () => {
res.setHeader('Content-Type', type);
s.pipe(res);
});
s.on('error', () => {
res.setHeader('Content-Type', 'text/plain');
res.statusCode = 404;
res.end('Not found');
});
});
server.listen(3000, () => console.log('Listening on http://localhost:3000/'));
This code is much more complicated and doesn't even serve properly files with other MIME types than the ones explicitly supported, which only demonstrates that it's much better to use module that does all of that properly.
So preferably use the right tool for the job, like express.static or any other well tested Node static file server and you will avoid a lot of the problems.
For more info see:
How to serve an image using nodejs
This is caused by your if statement. You should be using something like this:
if(req.url == '/index.html'){
// load index.html
}else if(req.url == '/hello.html'){
// load hello.html
}else if ... // the rest for css and js
Some explanations
When you check just the indexOf of the request url, you won't be able to differ index.html from hello.html (both have .html), so what you want to check is the file itself.
For that error, since any .html in the url matches both IFs, and you are not using if..else, it will enter both, calling end in the request in one IF, and trying to write after end in the other.
Side note
This should be used only for learning purposes. There is no need to use this method once you get a grasp on how it works and what's happening, because, when you do, just go with well-known tools that do the job in much simpler ways.
Related
I'm trying to download a file from another site from my Node app after an express HTTP get request and then return the file for download. I've tried multiple ways of getting the file, using pipe, blob, etc. but I'm grasping in the dark. The code might give you a bit more of an insight as to what I'm trying to achieve:
var router = require('express').Router();
var fs = require('fs');
var http = require('http');
router.get('/download/:file', function (req, res, next) {
http.get('http://anothersite/' + req.params.file, function(response) {
res.setHeader('Content-disposition', 'attachment; filename=' + req.params.file);
res.setHeader('Content-type', 'application/octet-stream');
res.download(fs.createWriteStream(req.params.file).pipe(response));
});
});
This gives me an error "Cannot pipe. Not Readable". The file itself is not a regular file format (it's a file from our customized software with its own extension).
What am I missing?
For one you need to use readable stream here, not writable
The Express res object is derived from node's http.ServerResponse, which is itself implementing node's WritableStream interface. See docs here and here.
Since that is the case, I think you can use response argument passed to your callback directly, since that is already a ReadableStream (see here). Try using readable stream like this:
router.get('/download/:file', function (req, res, next) {
http.get('http://anothersite/' + req.params.file, function(response) {
res.setHeader('Content-disposition', 'attachment; filename=' + req.params.file);
res.setHeader('Content-type', 'application/octet-stream');
response.pipe(res); // <-- change here
});
});
This code is working, with node v5.0.0 and latest chrome:
const express = require('express');
const app = express();
const http = require('http');
app.get('/', (req, res) => {
http.get('http://www.fillmurray.com/200/300', (response) => {
res.setHeader('Content-disposition', 'attachment; filename=' + 'hello.jpg');
res.setHeader('Content-type', 'application/octet-stream');
response.pipe(res)
});
});
app.listen(3001, () => console.log(('listening :)')))
you can use request library as:
request('http://anothersite/').pipe(fs.createWriteStream('filename.extension'))
Update:
If you are willing to do it by http you can create a write stream to as follow. After saving the file successfully I think you can do res.download() correctly.
var http = require('http')
http.get('http://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png').on('response', function (response) {
var writeStream = fs.createWriteStream('output.png');
response.pipe(writeStream);
response.on('end', function () {
console.log('stream completed!')
});
// This is here incase any errors occur
writeStream.on('error', function (err) {
console.log(err);
});
});
I am using Meteor as the backend to my ionic+angular webApp. I'm deploying the app using meteor-up. I have put my entire app in the Meteor /public folder and it works find when I access it like this:
http://localhost:3000/index.html
How can I set/rewrite/redirect the Meteor default page so I can load the same page from:
http://localhost:3000/ or http://localhost:3000/myApp
without losing my Meteor server
Here is the complete solution:
fs = Npm.require('fs');
crypto = Npm.require('crypto');
WebApp.connectHandlers.use("/", function(req, res, next) {
var data, filepath;
if (req.method !== 'GET') {
return next();
}
filepath = process.env.PWD + '/public/index.html';
// serve default file, with eTag,
// i.e. http://localhost:3000/
fs.readFile(filepath, function(err, buf) {
var eTag;
eTag = crypto.createHash('md5').update(buf).digest('hex');
if (req.headers['if-none-match'] === eTag) {
res.writeHead(304, 'Not Modified');
return res.end();
}
res.writeHead(200, {
'ETag': eTag,
'Content-Type': 'text/html'
});
return res.end(buf);
});
return;
// serve default file, without eTag or caching headers
// i.e. http://localhost:3000/
data = fs.readFileSync(filepath);
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.write(data);
return res.end();
// redirect to default file
// i.e. http://localhost:3000/index.html
res.writeHead(301, {
'Location': '/index.html'
});
return res.end();
});
I have a multi-level collection of .html, .js, .png, .css, etc files in a site. A peek at my site hiearchy looks like the following:
index.html
child1
index.html
page1.html
page2.html
...
child2
grandchild1
index.html
grandchild2
index.html
index.html
page1.html
page2.html
resources
css
myTheme.css
img
logo.png
profile.png
js
jquery.js
...
...
I am migrating this to run under Node.js. I have been told I MUST use RESTIFY. Currently, I've written the following for my server:
var restify = require('restify');
var fs = require('fs');
var mime = require('mime');
var server = restify.createServer({
name: 'Demo',
version: '1.0.0'
});
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.get('/', loadStaticFile);
server.get('/echo/:name', function (req, res, next) {
res.send(req.params);
return next();
});
server.listen(2000, function () {
console.log('Server Started');
});
function loadStaticFile(req, res, next) {
var filePath = __dirname + getFileName(req);
console.log("Returning " + filePath);
fs.readFile(filePath, function(err, data) {
if (err) {
res.writeHead(500);
res.end("");
next(err);
return;
}
res.contentType = mime.lookup(filename);
res.writeHead(200);
res.end(data);
return next();
});
}
function getFileName(req) {
var filename = "";
if (req.url.indexOf("/") == (req.url.length-1)) {
filename = req.url + "index.html";
} else {
console.log("What Now?");
}
return filename;
}
With this code, I can successfully load index.html. However, my index.html file references some JavaScript, image files, and style sheets. I can see via Fiddler that that these files are being requested. However, in my node.js console window, I never see "Returing [js|css|png filename]". Its like my node.js web server returns index.html and that's it.
What am I doing wrong?
The latest versions of restify has builtin middleware serveStatic() middleware that will do this for you.
from a http://mcavage.me/node-restify/#Server-API
server.get(/\/docs\/public\/?.*/, restify.serveStatic({
directory: './public'
}));
for more detailed example:
http://mushfiq.me/2013/11/02/serving-static-files-using-restify/
Do any of your served files contain relative paths (say ../abc.js)?
You have to use path.resolve() to get the real path for fs.readFile().
Anyway there are a lot of pitfalls in serving files:
invalid url (400)
file not found (404)
escape sequence (url encoding)
fs.read() read files into memory (by #robertklep)
etc
You can use existing static file serving middleware.
I've been using Ecstatic, AFAIK it handles those issues properly.
Try
server.use(ecstatic({ root: __dirname + '/' }));
If that fails you can refer to this to stack Restify on top of Connect/Express.
Im trying to learn node.js and have hit a bit of a roadblock.
My issue is that i couldn't seem to load an external css and js file into a html file.
GET http://localhost:8080/css/style.css 404 (Not Found)
GET http://localhost:8080/js/script.css 404 (Not Found)
(this was when all files were in the root of the app)
I was told to somewhat mimic the following app structure, add a route for the public dir to allow the webserver to serve the external files.
my app structure is like so
domain.com
app/
webserver.js
public/
chatclient.html
js/
script.js
css/
style.css
So my webserver.js script is in the root of app, and everything I want to access is in 'public'.
I also saw this example that uses path.extname() to get any files extentions located in a path. (see the last code block).
So I've tried to combine the new site structure and this path.extname() example, to have the webserver allow access to any file in my public dir, so I can render the html file, which references the external js and css files.
My webserver.js looks like this.
var http = require('http')
, url = require('url')
, fs = require('fs')
, path = require('path')
, server;
server = http.createServer(function(req,res){
var myPath = url.parse(req.url).pathname;
switch(myPath){
case '/public':
// get the extensions of the files inside this dir (.html, .js, .css)
var extname = mypath.extname(path);
switch (extname) {
// get the html
case '.html':
fs.readFile(__dirname + '/public/chatclient.html', function (err, data) {
if (err) return send404(res);
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data, 'utf8');
res.end();
});
break;
// get the script that /public/chatclient.html references
case '.js':
fs.readFile(__dirname + '/public/js/script.js', function (err, data) {
if (err) return send404(res);
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end(content, 'utf-8');
res.end();
});
break;
// get the styles that /public/chatclient.html references
case '.css':
fs.readFile(__dirname + '/public/css/style.css', function (err, data) {
if (err) return send404(res);
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end(content, 'utf-8');
res.end();
});
}
break;
default: send404(res);
}
});
Inside the case of public, I'm trying to get any of the folders/files inside of this dir via
var extname = mypath.extname(path);
Similar to the link I provided.
But at the moment 'extname' is empty when I console log it.
Can anyone advise what I might need to add or tweek here?
I'm aware this can be done easily in Express, but I would like to know how to achieve the same thing just relying on Node.
I's appreciate any help on this.
Thanks in advance.
There are several problems with your code.
Your server is not going to run as you have not specified a port to listen from.
As Eric pointed out your case condition will fail as 'public' does not appear in the url.
Your are referencing a non-existent variable 'content' in your js and css responses, should be 'data'.
You css content-type header should be text/css instead of text/javascript
Specifying 'utf8' in the body is unnecessary.
I have re-written your code.
Notice I do not use case/switch. I prefer much simpler if and else, you can put them back if that's your preference. The url and path modules are not necessary in my re-write, so I have removed them.
var http = require('http'),
fs = require('fs');
http.createServer(function (req, res) {
if(req.url.indexOf('.html') != -1){ //req.url has the pathname, check if it conatins '.html'
fs.readFile(__dirname + '/public/chatclient.html', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.js') != -1){ //req.url has the pathname, check if it conatins '.js'
fs.readFile(__dirname + '/public/js/script.js', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/javascript'});
res.write(data);
res.end();
});
}
if(req.url.indexOf('.css') != -1){ //req.url has the pathname, check if it conatins '.css'
fs.readFile(__dirname + '/public/css/style.css', function (err, data) {
if (err) console.log(err);
res.writeHead(200, {'Content-Type': 'text/css'});
res.write(data);
res.end();
});
}
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
You might want to look into using server frameworks like express which allow you to set a 'public' directory for automatically routing static files
var express = require('express'),app = express();
app.use(express.static(path.join(__dirname, 'public')));
The cheap overhead of such a framework would really be worth the effort of effectively 'reinventing the wheel'
public does not appear in the URL requested by the client, so the switch on myPath will always fall through.
You could consider looking at the Static middleware provided in Connect. Looking at Static's source code might give you some ideas on how to do this with node.js code (if you want learn how to do it without using an existing library).
// get the extensions of the files inside this dir (.html, .js, .css)
var extname = **mypath**.extname(path);
These are reversed. Should be:
var extension = path.extname(mypath);
I also do not use function names for variable names when I can avoid it.
Auto Update files on change, delay for update 1 sec.
Format : app.js | index.htm | style.css
// packages
const http = require('http');
const fs = require('fs');
// server properties
const hostname = '127.0.0.1';
const port = 3000;
const timer = 300;
//should trigger atualize function every timer parameter
let htmlfile = '';
let cssfile = '';
let jsfile = '';
uptodate();
// should read file from the disk for html
function uptodate()
{
console.log(1);
fs.readFile('./index.html', function (err, html) {
if (err) {
throw err;
}
htmlfile = html;
});
// should read css from the disk for css
fs.readFile('./style.css', function (err, html) {
if (err) {
throw err;
}
cssfile = html;
});
// should read js file from the disk
fs.readFile('./app.js', function (err, html) {
if (err) {
throw err;
}
jsfile = html;
});
setTimeout(function(){ uptodate(); }, 1000);
}
const server = http.createServer((req, res) => {
res.statusCode = 200;
// should send css and js
if(req.url.indexOf('.css') != -1){ //req.url has the pathname, check if it conatins '.js'
res.writeHead(200, {'Content-Type': 'text/css'});
res.write(cssfile);
res.end();
return;
}
if(req.url.indexOf('.js') != -1){ //req.url has the pathname, check if it conatins '.js'
res.writeHead(200, {'Content-Type': 'text/javascript'});
res.write(jsfile);
res.end();
return;
}
// should send html file via request
res.writeHeader(200, {"Content-Type": "text/html"});
res.write(htmlfile);
res.end();
});
// should send css and js
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
How do I get a node.js server to redirect users to a 404.html page when they enter an invalid url?
I did some searching, and it looks like most results are for Express, but I want to write my server in pure node.js.
The logic of determining a "wrong" url is specific to your application. It could be a simple file not found error or something else if you are doing a RESTful app. Once you've figured that out, sending a redirect is as simple as:
response.writeHead(302, {
'Location': 'your/404/path.html'
//add other headers here...
});
response.end();
If you are using ExpressJS, it's possible to use:
res.redirect('your/404/path.html');
To indicate a missing file/resource and serve a 404 page, you need not redirect. In the same request you must generate the response with the status code set to 404 and the content of your 404 HTML page as response body. Here is the sample code to demonstrate this in Node.js.
var http = require('http'),
fs = require('fs'),
util = require('util'),
url = require('url');
var server = http.createServer(function(req, res) {
if(url.parse(req.url).pathname == '/') {
res.writeHead(200, {'content-type': 'text/html'});
var rs = fs.createReadStream('index.html');
util.pump(rs, res);
} else {
res.writeHead(404, {'content-type': 'text/html'});
var rs = fs.createReadStream('404.html');
util.pump(rs, res);
}
});
server.listen(8080);
404 with Content/Body
res.writeHead(404, {'Content-Type': 'text/plain'}); // <- redirect
res.write("Looked everywhere, but couldn't find that page at all!\n"); // <- content!
res.end(); // that's all!
Redirect to Https
res.writeHead(302, {'Location': 'https://example.com' + req.url});
res.end();
Just consider where you use this (e.g. only for http request), so you don't get endless redirects ;-)
Try this:
this.statusCode = 302;
this.setHeader('Location', '/url/to/redirect');
this.end();
I used a switch statement, with the default as a 404:
var fs = require("fs");
var http = require("http");
function send404Response (response){
response.writeHead(404, {"Content-Type": "text/html"});
fs.createReadStream("./path/to/404.html").pipe(response);
}
function onRequest (request, response){
switch (request.url){
case "/page1":
//statements
break;
case "/page2":
//statements
break;
default:
//if no 'match' is found
send404Response(response);
break;
}
}
http.createServer(onRequest).listen(8080);
You have to use the following code:
response.writeHead(302 , {
'Location' : '/view/index.html' // This is your url which you want
});
response.end();
Use the following code this works fine in Native Nodejs
http.createServer(function (req, res) {
var q = url.parse(req.url, true);
if (q.pathname === '/') {
//Home page code
} else if (q.pathname === '/redirect-to-google') {
res.writeHead(301, { "Location": "http://google.com/" });
return res.end();
} else if (q.pathname === '/redirect-to-interal-page') {
res.writeHead(301, { "Location": "/path/within/site" });
return res.end();
} else {
//404 page code
}
res.end();
}).listen(8080);
use
res.redirect('/path/404.html');
or you can redirect to any defined URI as
res.redirect('/');