用node搭一个静态服务
生活随笔
收集整理的這篇文章主要介紹了
用node搭一个静态服务
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
如何搭一個靜態服務
- http-server 起服務
- mime chalk debug ejs 所需模塊
- http fs util path自帶模塊
- yargs
目錄結構
啟動一個服務需要 src/congfig文件
- 運行的條件 指定主機名
- 指定啟動的端口號
- 自定運行的目錄
通過config配置 app起服務 tepl編譯 bin和package.json鏈接命令行
配置
config.js
//將配置掛載在我們的實例上,方便后期手動指定,直接get就可以 let path = require('path') let config = {hostname : '127.0.0.1', //主機port:3000, //端口號dir:path.join(__dirname, 'public') } module.exports = config; 復制代碼tmpl
<!--ejs模版,當是文件夾的時候會用到--> <body><%dirs.map(item=>{%><li><a href="<%=item.pathname%>"><%=item.filename%></a></li> <%})%> </body> 復制代碼配置全局管理 (全局映射關系)
package.json
//添加bin配置 "bin": { "zdl": "bin/www.js" }, 復制代碼執行npm link
bin/www.js 用此文件作為入口,幫我們引用app.js
//yargs用法 在命令行輸入`zdl --help`測試是否生效 // 第一執行了命令后 會執行 bin/www.js let yargs = require('yargs') let argv = yargs.option('port', {alias: 'p',default: 3000, //默認demand: false, //是否必填description: 'this is port' }).option('hostname', {alias: 'host',default: 'localhost',type: String, //類型demand: false,description: 'this is hostname' }).option('dir', {alias: 'd',default: process.cwd(),type: String,demand: false,description: 'this is cwd' }).usage('zdl [options]').argv; //用法提示 //用此文件作為入口,幫我們引用app.js let Server = require('../src/app'); new Server(argv).start(); // 開啟服務 //相當于在app.js執行 //let server = new Server(argv); //server.start(); 復制代碼app.js
架子
// 實現一個靜態服務 let http = require('http'); let fs = require('fs'); let url = require('url'); let util = require('util'); let path = require('path');let mime = require('mime'); let ejs = require('ejs'); // 渲染模板 let chalk = require('chalk'); // 粉筆 let debug = require('debug')('*');//所有的都輸出 // 第二個參數可以指定環境變量在為什么值時才打印 // window set DEBUG=XXX export DEBUG=XXXXclass Server {constructor(args) { //config在調用此文件時傳參argsconstructor(args) {this.config = {...config,...args},this.template = template;//渲染模版}async handleRequest(req, res) { // 這里的this都是實例let { pathname } = url.parse(req.url, true);let p = path.join(this.config.dir, pathname);try{let statObj = await stat(p);if (statObj.isDirectory()) {//若是文件夾直接渲染數據let dirs = await readdir(p);dirs = dirs.map(dir => { return {filename: dir, pathname: path.join(pathname, dir)}}); let str = ejs.render(this.template, { dirs, title: 'ejs' });//template渲染模板,數據,渲染結果是字符串res.setHeader('Content-Type', 'text/html;charset=utf8');res.end(str);} else {// 文件 發送文件this.sendFile(req, res, p, statObj);}} catch (e) {// 文件不存在的情況this.sendError(req, res, e);}}//創建服務start() {let server = http.createServer(this.handleRequest.bind(this));//this.handleRequest,this是回調函數的this,也可以在該函數return 箭頭函數let { hostname, port } = this.config;debug(`http://${hostname}:${chalk.green(port)} start`)server.listen(port, hostname);}//錯誤處理sendError(){// 解析字符串打印對象//debug(util.inspect(e).toString());res.statusCode = 404;res.end(`Not Found`);}//發送文件sendFile(){...} }module.exports = Server; 復制代碼添加功能
添加的三個功能都是http的應用,想更具體請參考node~http緩存
class Server {// 添加緩存功能呢cache(req, res, p, stat) {}//添加壓縮功能gzip(req, res, p, stat) {}//添加范圍請求功能range(req, res, p, stat) {}//如果是文件sendFile(req, res, p, stat) {} } 復制代碼如果是目錄 就將目錄中的內容展現出來. 如果是文件就將文件展示出來
cache
// 添加緩存功能呢cache(req, res, p, stat) { //Catch-Control Expries if-modified-since if-none-matchlet since = req.headers['if-modified-since'];let match = req.headers['if-none-match'];let ssince = stat.ctime.toUTCString();let smatch = stat.ctime.getTime() + stat.size;res.setHeader('Last-Modified', ssince);res.setHeader('ETag', smatch);res.setHeader('Cache-Control', 'max-age=6');if (since != ssince) {debug(since, ssince);return false;}if (match != smatch) {debug(match, smatch);return false}return true;}//如果是文件sendFile(req, res, p, stat) {if (this.cache(req, res, p, stat)) {// 檢測是否有緩存res.statusCode = 304;res.end();return;}res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(res);} 復制代碼gzip
//添加壓縮功能gzip(req, res, p, stat) {let header = req.headers['accept-encoding'];if (header) {if (header.match(/\bgzip\b/)) {res.setHeader('Content-Encoding', 'gzip')return zlib.createGzip();} else if (header.match(/\bdeflate\b/)) {res.setHeader('Content-Encoding', 'deflate')return zlib.createDeflate();}} else {return false;}}//如果是文件sendFile(req, res, p, stat) {...let compress = this.gzip(req, res, p, stat);if (compress) { // 返回的是一個壓縮流res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p).pipe(compress).pipe(res);} else {res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p).pipe(res);}} 復制代碼range
//添加范圍請求功能range(req, res, p, stat) {let range = req.headers['range'];if(range){let [, start, end] = range.match(/(\d*)-(\d*)/) || [];start = start ? parseInt(start) : 0;end = end ? parseInt(end) : stat.size;res.statusCode = 206;res.setHeader('Accept-Ranges', 'bytes');res.setHeader('Content-Length', end - start + 1);res.setHeader('Content-Range', `bytes ${start}-${end}/${stat.size}`);return { start, end };}else{return {start:0,end:stat.size}}}//如果是文件sendFile(req, res, p, stat) {let { start, end } = this.range(req, res, p, stat);if (compress) { // 返回的是一個壓縮流res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(compress).pipe(res);} else {res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(res);}} 復制代碼小結
以下是整個app.js的內容
// 實現一個靜態服務 // 如果是目錄 就將目錄中的內容展現出來 // 如果是文件就將文件展示出來 let http = require('http'); let fs = require('fs'); let url = require('url'); let zlib = require('zlib'); let util = require('util'); let path = require('path');let mime = require('mime'); let ejs = require('ejs'); // 渲染模板 let chalk = require('chalk'); // 粉筆 // 第二個參數可以指定環境變量在為什么值時才打印 // window set DEBUG=XXX export DEBUG=XXXX let debug = require('debug')('*'); // 運行的條件 指定主機名 // 指定啟動的端口號 // 自定運行的目錄 let stat = util.promisify(fs.stat); let readdir = util.promisify(fs.readdir); let config = require('./config'); let template = fs.readFileSync(path.resolve(__dirname, 'tmpl.html'), 'utf8'); class Server {constructor(args) {this.config = { ...config, ...args };// 將配置掛載在我們的實例上this.template = template;}async handleRequest(req, res) { // 這里的this都是實例let { pathname } = url.parse(req.url, true);let p = path.join(this.config.dir, pathname);// 1.根據路徑 如果是文件夾 顯示文件夾里的內容// 2.如果是文件 顯示文件的內容try { // 如果沒錯誤說明文件存在let statObj = await stat(p);if (statObj.isDirectory()) {// 現在需要一個當前目錄下的解析出的對象或者數組let dirs = await readdir(p);dirs = dirs.map(dir => { // dirs就是要渲染的數據return {filename: dir,pathname: path.join(pathname, dir)}});let str = ejs.render(this.template, { dirs, title: 'ejs' });res.setHeader('Content-Type', 'text/html;charset=utf8');res.end(str);} else {// 文件 發送文件this.sendFile(req, res, p, statObj);}} catch (e) {// 文件不存在的情況this.sendError(req, res, e);}}// 實現其他功能// 實現范圍請求// 實現緩存// 服務器 Cache-Control Expires // Last-Modified ETag:ctime+size// 客戶端// if-modified-since if-none-matchcache(req, res, p, stat) {// 實現緩存 let since = req.headers['if-modified-since'];let match = req.headers['if-none-match'];let ssince = stat.ctime.toUTCString();let smatch = stat.ctime.getTime() + stat.size;res.setHeader('Last-Modified', ssince);res.setHeader('ETag', smatch);res.setHeader('Cache-Control', 'max-age=6');if (since != ssince) {debug(since, ssince);return false;}if (match != smatch) {debug(match, smatch);return false}return true;}// 實現服務端壓縮gzip(req, res, p, stat) {let header = req.headers['accept-encoding'];if (header) {if (header.match(/\bgzip\b/)) {res.setHeader('Content-Encoding', 'gzip')return zlib.createGzip();} else if (header.match(/\bdeflate\b/)) {res.setHeader('Content-Encoding', 'deflate')return zlib.createDeflate();}} else {return false;}}range(req, res, p, stat) {let range = req.headers['range'];if(range){let [, start, end] = range.match(/(\d*)-(\d*)/) || [];start = start ? parseInt(start) : 0;end = end ? parseInt(end) : stat.size;res.statusCode = 206;res.setHeader('Accept-Ranges', 'bytes');res.setHeader('Content-Length', end - start + 1);res.setHeader('Content-Range', `bytes ${start}-${end}/${stat.size}`);return { start, end };}else{return {start:0,end:stat.size}}}sendFile(req, res, p, stat) {if (this.cache(req, res, p, stat)) {// 檢測是否有緩存res.statusCode = 304;res.end();return};let compress = this.gzip(req, res, p, stat);let { start, end } = this.range(req, res, p, stat); //范圍請求,返回開始位置和結束位置if (compress) { // 返回的是一個壓縮流res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(compress).pipe(res);} else {res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');fs.createReadStream(p, { start, end }).pipe(res);}}sendError(req, res, e) {// 解析字符串打印對象//debug(util.inspect(e).toString());res.statusCode = 404;res.end(`Not Found`);}start() {let server = http.createServer(this.handleRequest.bind(this));let { hostname, port } = this.config;debug(`http://${hostname}:${chalk.green(port)} start`)server.listen(port, hostname);} } // 開啟一個服務 module.exports = Server 復制代碼現在你只需要在cmd里面輸入zdl就可以啟動一個靜態服務了
發布到npm
發包
在用的時候我們可以直接 npm install [name] 然后zdl運行
總結
以上是生活随笔為你收集整理的用node搭一个静态服务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二、1、怎么做都好做,没flag就抓包
- 下一篇: html基础1-基本语法/段落标签/特殊