本文展示是基于node.js的静态文件服务器,代码参考自这里,主要是练习node http、文件模块的使用,另外,对理解http协议也很有帮助
除了实现了基本的路由控制,还实现了MIME类型、304缓存、gzip压缩、目录读取

首先是配置文件,setting.js

var setting = {
webroot : '/xxx/xxx/webroot',
viewdir : false,
index : 'index.html', //只有当viewdir为false时,此设置才有用
expires : {
filematch : /^(gif|png|jpg|js|css)$/ig,
maxAge: 60 * 60 //默认为一个月
},
compress : {
match : /css|js|html/ig
}
};
module.exports = setting;

MIME映射,mime.js

var mime = {
"html": "text/html",
"ico": "image/x-icon",
"css": "text/css",
"gif": "image/gif",
"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"
};
module.exports = mime;

然后是主程序,server.js

var http = require('http');
var setting = require('./setting.js');
var mime = require('./mime');
var url = require('url');
var util = require('util');
var path = require('path');
var fs = require('fs');
var zlib = require('zlib');
//访问统计
var number = 0;
var accesses = {};
 
http.createServer(function(req, res){
res.number = ++number;
accesses[res.number] = {startTime : new Date().getTime()};
var pathname = url.parse(req.url).pathname;
//安全问题,禁止父路径
pathname = pathname.replace(/\.\./g, '');
var realPath = setting.webroot + pathname;
accesses[res.number].path = pathname;
readPath(req, res, realPath, pathname);
}).listen(8000);
 
console.log('http server start at parth 8000\n\n\n');
//判断文件是否存在
function readPath(req, res, realPath, pathname){
//首先判断所请求的资源是否存在
path.exists(realPath, function(ex){
console.log('path.exists--%s', ex);
if(!ex){
responseWrite(res, 404, {'Content-Type' : 'text/plain'},
'This request URL ' + pathname + ' was not found on this server.');
}else{
//文件类型
fs.stat(realPath, function(err, stat){
if(err){
responseWrite(res, 500, err);
}else{
//目录
if(stat.isDirectory()){
//是否读取目录
if(setting.viewdir){
fs.readdir(realPath, function(err, files){
if(err){
responseWrite(res, 500, err);
}else{
var htm = '<html><head><title>' + pathname + '</title></head><body>' + pathname + '<hr>';
for(var i = 0; i < files.length; i++){
htm += '<br><a href="' + pathname + (pathname.slice(-1) != '/' ? '/' : '')
+ files[i] + '">' + files[i] + '</a>', 'utf8';
}
responseWrite(res, 200, {'Content-Type' : 'text/html'}, htm);
}
});
}else if(setting.index && realPath.indexOf(setting.index) < 0){
readPath(req, res, path.join(realPath, '/', setting.index), path.join(pathname, '/', setting.index));
}else{
responseWrite(res, 404, {'Content-Type' : 'text/plain'},
'This request URL ' + pathname + ' was not found on this server.');
}
}else{
var type = path.extname(realPath);
type = type ? type.slice(1) : 'nuknown';
var header = {'Content-Type' : mime[type] || 'text/plain'};
//缓存支持
if(setting.expires && setting.expires.filematch
&& type.match(setting.expires.filematch)){
var expires = new Date(),
maxAge = setting.expires.maxAge || 3600 * 30;
expires.setTime(expires.getTime() + maxAge * 1000);
header['Expires'] = expires.toUTCString();
header['Cache-Control'] = 'max-age=' + maxAge;
var lastModified = stat.mtime.toUTCString();
header['Last-Modified'] = lastModified;
//判断是否304
if(req.headers['if-modified-since'] && lastModified == req.headers['if-modified-since']){
responseWrite(res, 304, 'Not Modified');
}else{
readFile(req, res, realPath, header, type);
}
}else{
readFile(req, res, realPath, header, type);
}
}
}
});
}
});
}
//读文件/压缩/输出
function readFile(req, res, realPath, header, type){
var raw = fs.createReadStream(realPath), cFun;
//是否gzip
if(setting.compress && setting.compress.match
&& type.match(setting.compress.match) && req.headers['accept-encoding']){
if(req.headers['accept-encoding'].match(/\bgzip\b/)){
header['Content-Encoding'] = 'gzip';
cFun = 'createGzip';
}else if(req.headers['accept-encoding'].match(/\bdeflate\b/)){
header['Content-Encoding'] = 'deflate';
cFun = 'createDeflate';
}
}
res.writeHead(200, header);
if(cFun){
raw.pipe(zlib[cFun]()).pipe(res);
}else{
raw.pipe(res);
}
}
//普通输出
function responseWrite(res, starus, header, output, encoding){
encoding = encoding || 'utf8';
res.writeHead(starus, header);
if(output){
res.write(output, encoding);
}
res.end();
accesses[res.number].endTime = new Date().getTime();
//日志输出
console.log('access[%s]--%s--%s--%s--%s\n\n', res.number, accesses[res.number].path,
(accesses[res.number].endTime - accesses[res.number].startTime),
starus, (output ? output.length : 0));
delete accesses[res.number];
}

over!
尚欠缺的功能:日志记录、断点、容错等~~以后有时间再加啦

nodejs入门-静态文件服务器的更多相关文章

  1. 从零开始,在windows上用nodejs搭建一个静态文件服务器

    从零开始,在windows上用nodejs搭建一个静态文件服务器 首先安装nodejs: 新建一个node文件夹 下载node.exe到该文件夹 下载npm然后解压到该文件夹 现在node文件夹是这样 ...

  2. [转载]用NodeJS打造你的静态文件服务器

    http://www.open-open.com/bbs/view/1321344823593 本文是我对V5Node项目的总结,该项目的特性包括: 项目大多数的文件都是属于静态文件,只有数据部分存在 ...

  3. 转:nginx入门指南,快速搭建静态文件服务器和代理服务器

    本文介绍 Nginx 入门基础知识,让你迅速搭建 Nginx 服务器.主要内容包括 Nginx 安装和简单使用.Nginx的简单原理.Nginx 配置文件的结构.如何使用 Nginx 来提供静态文件服 ...

  4. 我的前端之旅-nodejs 安装静态的文件服务器 (1)

    一个最简单的 Web Server 之功能包含下列三个步骤:步骤一 : 接收浏览器所传来的网址:步骤二 : 取出相对应的文件:步骤三 : 将文件内容传回给浏览器.然而.在这个接收与传回的过程中,所有的 ...

  5. Node.js静态文件服务器实战[转]

    p.s. 在下面这篇文章的指导下,做了一个静态文件服务器,见:https://github.com/walkerwzy/node_static_server ==== 这是一篇阐述得比较详细的文章,从 ...

  6. Anywhere 随启随用的静态文件服务器

    三江建材官网项目 写nodeJs系列的文章都是因为这一个项目 第一天,搭建项目环境 记录心情: 首先,在写这个项目的时候,我很无助,只是拿到了设计稿,还有一个指导人,平常会很忙,只有在休闲的时候才能动 ...

  7. nodeJs 5.0.0 安装配置与nodeJs入门例子学习

    新手学习笔记,高手请自动略过 安装可以先看这篇:http://blog.csdn.net/bushizhuanjia/article/details/7915017 1.首先到官网去下载exe,或者m ...

  8. NodeJS入门(四)—— path对象

    很快Node就会迎来4.0的时代,届时将并入现有的iojs,所以先前写过的iojs入门系列直接更名为NodeJS入门. 本篇开始将逐个介绍Node的各主要模块,依循API文档走一遍,但会给出比API文 ...

  9. nodejs express 静态文件的路径

    当express 设置为静态文件服务器的时候.可以通过2种方式进行方位: 1,通过设置app.use('路径1','../a/b/image') express 路径的形式,如 src="路 ...

随机推荐

  1. 第二百零六节,jQuery EasyUI,Menu(菜单)组件

    jQuery EasyUI,Menu(菜单)组件 学习要点: 1.加载方式 2.菜单项属性 3.菜单属性 4.菜单事件 5.菜单方法 本节课重点了解 EasyUI 中 Menu(菜单)组件的使用方法, ...

  2. WPF 附加属性的用法 (一)

    public class MDCTest { public static DependencyProperty MouseDoubleClickCommandProperty = Dependency ...

  3. sql 存储过程调用函数

    /****************************************************************************** ** Name: usp_biz_Con ...

  4. Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0下面)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40411921.本文出自:[张鸿洋的博客] 1.概述 之前写过一篇博文:Andro ...

  5. HDU4267(2012年长春站)

    这道题真的是好题,让我对线段树有了全新的认识,至少让我真正感受到了线段树的神奇. 题意是就是线段树区间更新,单点询问的问题,不过这个题好就好在它的区间更新的点并不连续! adding c to eac ...

  6. Exchange 2016 系统要求

    Exchange 2016 和早期版本的 Exchange Server 共存方案 Exchange 2016支持混合部署方案 Exchange 2016 的网络和目录服务器要求 目录服务体系结构: ...

  7. Python SQLAlchemy基本操作和常用技巧包含大量实例,非常好python

    http://www.makaidong.com/%E8%84%9A%E6%9C%AC%E4%B9%8B%E5%AE%B6/28053.shtml "Python SQLAlchemy基本操 ...

  8. MIS货物拆包销售的问题

    就是不能拆包装销售.比如一箱香烟要一包包的卖,一箱里面有50条,一条里面有10包,而是,要一包一包的卖. 解决方案:入库的时候,记录下包装总量(自动改成数量×50),再附加2条说明字段,第一条说明是一 ...

  9. Tickets---hdu1260(简单dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1260 题意是有n个人排队买票,第 i 个人买票所需要的时间是a[i],这个人和 i-1 或者 i+1 ...

  10. django-应用中和amdin使用富文本编辑器kindeditor

    文章描述.新闻详情和产品介绍等,都需要大量的文字描述信息或图片.视频.文字的编辑等,这个时候我们就需要介绍第三方富文本编辑器. 今天介绍的是django中绑定和应用kindeditor编辑器: 效果如 ...