对于http
的了解,以前只看下200
还有404
之类。稍微学习node
的http
后发现了很多平时应用的到但不易察觉的功能。 比如请求响应中的压缩,图片防盗链的应用等等。了解到应用node核心模块http
等可以搭建本地服务,小试一下~
现在,就利用http搭建一个小巧的本地服务。
创建文件基本目录
|--my-server
| |--bin
| | |--www.js
| |--src
| | |--config.js
| | |--index.js
| | |--template.html
| |--public
| |--index.html
| |--index.css
| |--a
| |--test.txt
复制代码
起步
-
www.js 做入口
#! /usr/bin/env node require('../src/index') 复制代码
#! /usr/bin/env node
为指定脚本的解释程序。这种写法主要是为了让你的程序在不同的系统上都能适用。
#!/usr/bin/env node
会自动的在你的用户PATH变量中所定义的目录中寻找node来执行的。 -
index.js 为项目主要内容,http在此引用并搭建服务。
let http = require('http') http.createServer(function(req,res){ res.end('hello world') }).listen(3000) 复制代码
绑定命令
npm link
会绑定package.json
里bin
下的命令。
{
"bin": {
"run-server": "bin/www.js"
}
}
复制代码
执行npm link
结果,显示命令成功以www.js
为入口:
up to date in 0.143s
/usr/local/bin/run-server -> /usr/local/lib/node_modules/http-jyn-static/bin/www.js
/usr/local/lib/node_modules/http-jyn-static -> /Users/jyn/work/study/june/static_server/jyn_server
复制代码
此时进行如下操作测试:
- 执行
run-server
,启动服务 - 浏览器请求
localhost://3002
- 测试成功,页面返回
hello world
服务实现
添加配置文件config.js:
module.exports = {
port: 3002,
host: 'localhost',
dir: public
}
复制代码
实现打开网页后可以将目录下的内容展示给用户,还可以进行对应的点击操作。
创建serevr
作为可以复用的静态服务,需要将server写成构造函数类型
class Server{
constructor(){
this.config = config
}
handleRequest(){
console.log(this)
}
start(){
let server = http.createServer(this.handleRequest.bind(this))// 让this指向当前的server
server.listen(config.port,config.host,()=>{
console.log(`server start http://${config.host}:${config.port}`)
})
}
}
复制代码
运行结果如下:
/usr/local/bin/node /Users/jyn/work/study/june/static_server/jyn_server/src/index.js
server start http://localhost:3002
复制代码
说明启动命令可以启动服务。 基本搭建结束,继续完善:
展示文件目录
promisify应用
通过promisify方便书写代码,处理文件读取等异步问题
结合promisify,获取文件信息,可方便后面分析文件路径,读取文件等:
let stat = util.promisify(fs.stat);// promisify
let readdir = util.promisify(fs.readdir);
let p = path.join(dir, pathname);
let statObj = await stat(p);
复制代码
目录分析
statObj.isDirectory()
根据目录类型还有文件类型进行相应处理; 通过读取当前访问的目录下的所有内容 readdir 数组,然后再把数组渲染回页面
if (statObj.isDirectory()) {
res.setHeader('Content-Type', 'text/html;charset=utf8')
let dirs = await readdir(p);
dirs = dirs.map(item=>({
name:item,
href:path.join(pathname,item)
}))
res.end(str);
} else {
this.sendFile(req, res, statObj, p);
}
复制代码
文件路径dirs结果为:
[{ name: 'a', href: '/a' },
{ name: 'test.txt', href: '/test.txt' },
{ name: 'index.css', href: '/index.css' },
{ name: 'index.html', href: '/index.html' } ]
复制代码
文件路径问题:
根据文件层级进行相应的拼接,点击第二层时 需要带上第一层的路径,可以通过直接拼接路径的方式:
dirs = dirs.map(item=>({
name:item,
href:path.join(pathname,item)
}))
复制代码
此时测试点击目录a,显示结果为:
[ { name: 'test.txt', href: '/a/test.txt' } ]
复制代码
应用ejs模板展示文件目录
<% '脚本' 标签,用于流程控制,无输出。
%> 一般结束标签
模板中javascript语句用特殊标签包裹:
<body>
<h2><%=name%></h2>
<%arr.forEach(item=>{%>
<li><a href="<%=item.href%>"><%=item.name%></a></li>
<%})%>
</body>
复制代码
根据ejs模板渲染数据方法ejs.render
进行解析,ejs.render()
用法为:
template(data); // => 输出绘制后的 HTML 字符串 ejs.render(str, data, options); // => 输出绘制后的 HTML 字符串 ejs.renderFile(filename, data, options, function(err, str){ // str => 输出绘制后的 HTML 字符串 }); 复制代码
应用ejs.render渲染后,设置响应头为text/html
,让浏览器解析模板字符串并展示
res.setHeader('Content-Type', 'text/html;charset=utf8')
let str = ejs.render(this.template, {
name: `Index of ${pathname}`,
arr: dirs
});
res.end(str);
复制代码
效果如图:
此时访问目录下文件列表已经成功,下一步需要根据点击进行文件返回或其他处理。返回文件内容
返回文件的内容需要根据文件的不同类型设置返回头,文件类型可应用第三方模块mime
, 通过mimelite.getType(...);
获取并读取文件内容:
res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');
fs.createReadStream(p).pipe(res);
复制代码
实现启动命令后自动启动端口
根据node核心模块 os, 根据不同的操作系统实现不同的打开方式:
The os.platform() method returns a string identifying the operating system platform as set during compile time of Node.js.
www.js
中添加
let os = require('os');
let {exec} = require('child_process')
if(os.platform() === 'win32'){
exec(`start http://${commander.host}:${commander.port}`);
}else{
exec(`open http://${commander.host}:${commander.port}`);
}
复制代码
此时操作之需要一步执行 run-server
,启动服务就行了,这时就会看到浏览器自动打开了localhost://3002
.
优化
基本功能实现后,可以对请求添加缓存机制,文件请求格式gzip压缩等功能。比较简单,会在下篇关于请求头响应头应用的文章中作介绍。
发包
- npm adduser --输入用户,密码,邮箱
- npm publish 出现错误:
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! no_perms Private mode enable, only admin can publish this module: http-jyn-static
复制代码
修正方案: npm config set registry http://registry.npmjs.org
之后再执行一遍发包操作,成功:
➜ jyn_server git:(master) npm publish
+ http-jyn-static@1.0.0
复制代码
下载使用
在一个拥有如下文件的目录进行静态服务安装:
|--test
| |--index.html
| |--config.js
| |--a
| |--read.txt
复制代码
安装
➜ test
npm install http-jyn-static
复制代码
启动服务
run-server
server start http://localhost:3002
复制代码
运行结果: 出现错误,因为件config.js中路径写死为public
文件夹,修改为: dir: process.cwd()
。
进行版本升级:
npm version minor
npm publish
再次更新package进行测试,成功打开文件夹列表:
以上简易版静态服务就算完工啦! 当然,我们还可以对本服务做缓存策略,以及文件格式压缩等优化。涉及到响应头这些基础应用,我会集中再写一篇。目前的工程化项目以及各种脚手架都集成了静态服务无需再手动搭建静态服务。这里的静态服务可以作为了解脚手架的一个热身运动吧。也可以提供给做小项目的同学使用。
欢迎大家批评指正~
Reference:
Author: Yanni Jia
Nickname: 非常兔
Email: 385067638@qq.com