node入门这个教程简单却生动有趣,涵盖了服务器端Javascript,函数式编程,阻塞与非阻塞,回调,Listener事件以及内部外部模块等许多内容。跟着这个教程把代码敲打一边,也算做了个简单的node服务器端代码框架,再要加handler就很方便了。
npm install formidable
需要安装第三方模块formidable,编写下面
四个文件
服务器入口文件 index.js
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
handle["/show"] = requestHandlers.show;
server.start(router.route, handle);
服务器文件 server.js
var http = require("http");
var url = require("url");
function start(route, handle){
var server = http.createServer(function(request,response){
var pathname = url.parse(request.url).pathname;
console.log("Requesti for " + pathname + " received.");
route(handle, pathname, response, request);
});
server.listen(8888);
console.log("Server has started.");
}
exports.start = start;
路由器 router.js
//
// 路由函数
//
function route(handle, pathname, response, request){
console.log("Route the request for " + pathname);
if(typeof handle[pathname] === "function"){
handle[pathname](response, request);
} else {
console.log("no handler set for " + pathname);
response.writeHead(404, {"Content-Type":"text/plain"});
response.write("404 not found");
response.end();
}
}
exports.route = route;
handler处理器 requestHandlers.js
var querystring = require("querystring"),
fs = require("fs"),
formidable = require("formidable");
var url = require("url");
//
// 显示上传表单
//
function start(response, request){
console.log("Request Handler 'start' was called.");
var body = '' +
'' +
'' +
'' +
'' +
'' +
'' +
'';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
//
// 使用formidable库,设置回调函数
//
function upload(response, request){
console.log("Request Handler 'upload' was called.");
var form = new formidable.IncomingForm();
console.log("start to parse");
form.parse(request, function(error, fields, files){
console.log("parsing done");
fs.renameSync(files.upload.path, "./test.png");
response.writeHead(200, {"Content-Type":"text/plain"});
response.write("received image:
");
response.write("");
response.end();
});
}
//
// 显示上传图片,通过回调函数输出
//
function show(response,request){
console.log("Request Handler 'show' was called.");
fs.readFile("./test.png", "binary", function(error, file){
if(error){
response.writeHead(500, {"Content-Type":"text/plain"});
response.write(error + "\n");
response.end();
} else {
response.writeHead(200, {"Content-Type":"image/png"});
response.write(file, "binary");
response.end();
}
});
}
//
// 导出handlers供router配置使用
//
exports.start = start;
exports.upload = upload;
exports.show = show;
运行方法node index.js,可以看到实现了文件上传展示的功能。
再要实现其它功能只需要添加自定义的handler并注册到路由上即可。
再参考这篇博文介绍加上静态文件服务功能,最后代码如下
配置文件config.js(网站根目录,资源过期时间配置,压缩选项等设置)
//
// 配置过期事件的文件类型
//
exports.Expires = {
fileMatch : /^(gif|png|jpg|js|css)$/ig,
maxAge: 60 * 60 * 24
};
//
// 配置需要gzip压缩的文件类型
//
exports.Compress = {
fileMatch : /^(css|js|html)$/ig
};
//
// 配置目录首页
//
exports.Welcome = {
file: "index.html"
};
//
// 配置网站根目录
//
exports.Webroot = {
path: "webroot"
};
mime.js自定义静态文件类型
//
// mime tyoe
//
exports.types = {
"css": "text/css",
"gif": "image/gif",
"html": "text/html",
"ico": "image/x-icon",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"js": "text/javascript",
"json": "application/json",
"pdf": "application/pdf",
"png": "image/png",
"svg": "image/svg+xml",
"swf": "application/x-shockwave-flash",
"tiff": "image/tiff",
"txt": "text/plain",
"wav": "audio/x-wav",
"wma": "audio/x-ms-wma",
"wmv": "video/x-ms-wmv",
"xml": "text/xml"
};
router.js修改路由实现静态文件服务器功能
var path = require("path");
var fs = require("fs");
var mime = require("./mime").types;
var config = require("./config");
var zlib = require("zlib");
//
// router
//
function route(handle, pathname, response, request){
console.log("Route the request for " + pathname);
var realpath = config.Webroot.path + pathname;
fs.stat(realpath, function(err, stat){
//
// Handlers
//
if(err)
{
if(typeof handle[pathname] === "function"){
handle[pathname](response, request);
}
// file not exists
else {
response.writeHead(404, {"Content-Type":"text/plain"});
response.write("404 not found");
response.end();
}
return;
}
//
// change /dir to /dir/index.html
//
if(stat.isDirectory()) {
realpath = path.join(realpath, "/", config.Welcome.file);
}
//
// exists Files
//
fs.exists(realpath, function(exists){
if(!exists) {
response.writeHead(404, {"Content-Type":"text/plain"});
response.write("404 not found");
response.end();
return;
}
//
// set last-modified
//
var lastModified = stat.mtime.toUTCString();
response.setHeader("Last-Modified", lastModified);
if(request.headers["if-modified-since"] && lastModified == request.headers["if-modified-since"]) {
response.writeHead(304, "Not Modified");
response.end();
return;
}
var ext = path.extname(realpath);
ext = ext ? ext.slice(1) : 'unknown';
//
// set content-type
//
var contentType = mime[ext] || "text/plain";
response.setHeader("Content-Type", contentType);
//
// set expire time
//
if(ext.match(config.Expires.fileMatch)) {
var expires = new Date();
expires.setTime(expires.getTime() +
config.Expires.maxAge * 1000);
response.setHeader("Expires",
expires.toUTCString());
response.setHeader("Cach-Control",
"max-age=" + config.Expires.maxAge);
}
var raw = fs.createReadStream(realpath);
var acceptEncoding = request.headers['accept-encoding'] || "";
var match = ext.match(config.Compress.fileMatch);
//
// compress and echo
//
if(match && acceptEncoding.match(/\bgzip\b/)){
response.writeHead(200,
{"Content-Encoding":"gzip"}
);
raw.pipe(zlib.createGzip()).pipe(response);
} else if(match && acceptEncoding.match(/\bdeflate\b/)){
response.writeHead(200,
{"Content-Encoding":"deflate"}
);
raw.pipe(zlib.createDeflate()).pipe(response);
} else {
response.writeHead(200, "Ok");
raw.pipe(response);
}
});
});
}
exports.route = route;
经过上面的修改,不但能自定义handle处理请求,也能支持静态文件(html,css,图片等),其中静态文件全部放在webroot目录下。当然最好还是使用现成的express框架。