首先先构建好项目目录,项目目录如下:
要启动一个服务器,我们需要知道这个服务器的启动时的端口号,在config.js配置一下:
- let config = {
- host:'localhost' //提示用 ,
- port:8080 //服务器启动时候的默认端口号,
- path:path.resolve(__dirname,'..','test-dir') //静态服务器启动时默认的工作目录
- }
读取静态文件之前首先要先启动服务器,之后所有的方法都在class Server方法里
- //handlebar 编译模板,得到一个渲染的方法,然后传入实际数据数据就可以得到渲染后的HTML了
- function list() {
- let tmpl = fs.readFileSync(path.resolve(__dirname, 'template', 'list.html'),'utf8');
- return handlebars.compile(tmpl);//进行编译,***渲染
- }class Server {
- constructor(argv) {
- this.list = list();
- this.config = Object.assign({}, this.config, argv);
- }
- start() {
- let server = http.createServer();//创建服务器
- //当客户端向服务端发出数据的时候,会出发request事件
- server.on('request', this.request.bind(this));
- server.listen(this.config.port, () => {//监听端口号
- let url = `http://${this.config.host}:${this.config.port}`;
- debug(`server started at ${chalk.green(url)}`);
- });
- }
- //发送错误信息,
- sendError(err, req, res) {
- res.statusCode = 500;
- res.end(`${err.toString()}`);
- }}
- module.exports = Server;
读取静态文件
- async request(req, res) {
- //先取到客户端想要的是文件或文件夹路径
- let { pathname } = url.parse(req.url);//获取路径的文件信息
- let filepath = path.join(this.config.root, pathname);//服务器上的对应服务器物理路径
- try {
- let statObj = await stat(filepath);//获取路径的文件信息
- if (statObj.isDirectory()) {//如果是目录的话,应该显示目录 下面的文件列表
- let files = await readdir(filepath);//读取文件的文件列表
- files = files.map(file => ({//把每个字符串变成对象
- name: file,
- url: path.join(pathname, file)
- }));
- //handlebar 编译模板
- let html = this.list({
- title: pathname,
- files
- });
- res.setHeader('Content-Type', 'text/html');设置请求头
- res.end(html);
- } else {
- this.sendFile(req, res, filepath, statObj);//读取文件
- }
- } catch (e) {//不存在访问内就发送错误信息
- debug(inspect(e));//inspect把一个对象转成字符
- this.sendError(e, req, res);
- }
- }
缓存支持/控制
设计思路
缓存分为强制缓存和对比缓存:
通过***修改时间来判断缓存是否可用
ETag是资源标签。如果资源没有变化它就不会变。
- handleCache(req, res, filepath, statObj) {
- let ifModifiedSince = req.headers['if-modified-since'];
- let isNoneMatch = req.headers['is-none-match'];
- res.setHeader('Cache-Control', 'private,max-age=30');//max-age=30缓存内容将在30秒后失效
- res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toGMTString());
- let etag = statObj.size;
- let lastModified = statObj.ctime.toGMTString();
- res.setHeader('ETag', etag);//获取ETag
- res.setHeader('Last-Modified', lastModified);//服务器文件的***修改时间
- //任何一个对比缓存头不匹配,则不走缓存
- if (isNoneMatch && isNoneMatch != etag) {//缓存过期
- return fasle;
- }
- if (ifModifiedSince && ifModifiedSince != lastModified) {//缓存过期
- return fasle;
- }
- //当请求中存在任何一个对比缓存头,则返回304,否则不走缓存
- if (isNoneMatch || ifModifiedSince) {//缓存有效
- res.writeHead(304);
- res.end();
- return true;
- } else {
- return false;
- }
- }
支持gzip压缩
设计思路
浏览器都会携带自己支持的压缩类型,最常用的两种是gzip和deflate。根据请求头Accept-Encoding,返回不同的压缩格式.
- getEncoding(req, res) {
- let acceptEncoding = req.headers['accept-encoding'];//获取客户端发送的压缩请求头的信息
- if (/\bgzip\b/.test(acceptEncoding)) {//如果是gzip的格式
- res.setHeader('Content-Encoding', 'gzip');
- return zlib.createGzip();
- } else if (/\bdeflate\b/.test(acceptEncoding)) {//如果是deflate的格式
- res.setHeader('Content-Encoding', 'deflate');
- return zlib.createDeflate();
- } else {
- return null;//不压缩
- }
- }
Range支持,断点续传
设计思路
- getStream(req, res, filepath, statObj) {
- let start = 0;//可读流起始位置
- let end = statObj.size - 1;//可读流结束位置
- let range = req.headers['range'];//获取客户端的range请求头信息,
- if (range) {//断点续传
- res.setHeader('Accept-Range', 'bytes');
- res.statusCode = 206;//返回整个内容的一块
- let result = range.match(/bytes=(\d*)-(\d*)/);//断点续传的分段内容不能有小数,网络传输的最小单位为一个字节
- if (result) {
- start = isNaN(result[1]) ? start : parseInt(result[1]);
- end = isNaN(result[2]) ? end : parseInt(result[2]) - 1;
- }
- }
- return fs.createReadStream(filepath, {
- start, end
- });
- }
发布为可执行命令
首先在package.json配置一下"bin": { "http-static": "bin/www" }
- #! /usr/bin/env node //这段代码一定要写在开头,为了兼容各个电脑平台的差异性
- // -d --root 静态文件目录 -o --host 主机 -p --port 端口号let yargs = require('yargs');
- let Server = require('../src/app.js');
- let argv = yargs.option('d',{
- alias:'root',
- demand:'false',
- type:'string',
- default:process.cwd(),
- description:'静态文件跟目录' })
- .option('o',{
- alias:'host',
- demand:'localhost',
- type:'string',
- description:'请配置监听的主机'})
- .option('p',{
- alias:'root',
- demand:'false',
- type:'number',
- default:8080,
- description:'请配置端口号'})
- .usage('http-static [options]').example(
- 'http-static -d / 8080 -o localhost','在本机的9090端口上监听客户端的请求'
- ).help('h').argv;
- // argv = {d,root,o,host,p,port}let server = new Server(argv);//启动服务server.start();
这样命令行当中通过输入http-static来直接启动静态文件服务器了,那么命令行调用的功能也就实现了,***用npm publish发布一下,发布到npm上面去了,我们就可以通过npm install -g来进行全局安装了。
服务器稳定性是最重要的,如果在稳定性方面不能够保证业务运行的需要,再高的性...
SQL的全称是Structured Query Language,翻译成中国话就是结构化查询语言。这是...
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,...
【51CTO.com原创稿件】如今,大部分企业已经把数字化转型作为企业的战略核心,并...
云计算时代,企业应用和数据完全上云后,如果数据丢失,找不到了,企业应该怎么...
背景 有一台腾讯的Linux云主机,在服务器上部署了一个docker(称为ServiceDocker...
Nginx 是一个采用主从架构的 Web 服务器,可用于反向代理、负载均衡器、邮件代理...
1.你当初对我许下的承诺,如今却要兑现在别人的身上。 2.我们还是未成年,却已...
如果说2020年教会了技术领导者什么,那就是需要应对意料之外的事情,因为冠状病...
【51CTO.com原创稿件】在数据存储管理领域,有这么一家成立多年,却并不为大家所...