Introducing dependency modules
let config = require('./config');
let chalk = require('chalk');// Chalk module (command line color change)
let http = require('http');/ / HTTP module
let fs = require('fs');// File module
let zlib = require('zlib');
let handlebars = require('handlebars');// Template engine
let url = require('url');// The url module parses the request URL
let mime = require('mime');// The MIME module gets the content type from the file name/path
let path = require('path');
let {promisify,inspect} = require('util');
let stat = promisify(fs.stat);
let readdir = promisify(fs.readdir);
process.env.DEBUG = 'static:*';
let debug = require('debug') ('static:app');
Copy the code
Compile template function
function list() {
// Compile the template, get the render method, pass in the data
let tmpl = fs.readFileSync(path.resolve(__dirname,'src/template'.'list.html'),'utf8');
return handlebars.compile(tmpl);// Return a function
}
Copy the code
This function is used to compile templates and returns a list of files to be returned to the client.
The start method starts the service
start(){
// Start the service
let server = http.createServer();
server.on('request'.this.request.bind(this));
server.listen(this.config.port,()=>{
let url = `The ${this.config.host}:The ${this.config.port}`;
debug(`start at ${chalk.green(url)}`); })}Copy the code
This.config. port can define its own port, and debug is used to print messages on the console.
Request handling method
async request(req,res){
// Receive the request
let { pathname } = url.parse(req.url);
let filepath = path.join(this.config.root,pathname);
try{
let statObj = await stat(filepath);
if(statObj.isDirectory()){// Check whether it is a directory
// Use the Handlebars template engine to render directories
let files = await readdir(filepath);// Array of strings
files = files.map((file) = >{
return {
name:file,
url:path.join(pathname,file)
}
});
let html = this.list({
title:pathname,
files:files
});
res.setHeader('Content-Type'.'text/html');
this.sendFile(req,res,html,statObj,'html');
}else{
this.sendFile(req,res,filepath,statObj,' '); }}catch (e){
debug(inspect(e));
this.sendError(req,res)
}
}
Copy the code
This is the main processing logic, first by parsing the client request URL, through the path module to get the file path, the first parameter is the static file root directory configured in config; To determine whether the request is for a file or directory, a try catch block is used to catch the exception. If the request is for a directory, all files in the directory are iterated and the file name and file path are passed to the list compilation template, which is then passed to the sending method.
Sending processing method
sendFile(req,res,filepath,statObj,type){
// Send the file
let expires = new Date(Date.now() + 60 * 1000);
// Cache Settings
res.setHeader('Expires', expires.toUTCString());
res.setHeader('Cache-Control'.'max-age=600');
res.setHeader('Accept-Range'.'bytes');// Resumable
if(type == 'html'){
res.end(filepath);
}else {
res.setHeader('Content-Type',mime.getType(filepath)); fs.createReadStream(filepath).pipe(res); }}Copy the code
In the send method, the cache policy is set to force caching, and the zlibFn method is called in the send method to see if the client request uses compression, and finally the content pipe to res is returned to the client.
Zlib compression method
zlibFn(req,res){
let acceptEncoding = request.headers['accept-encoding'];/ / compression
if(! acceptEncoding) { acceptEncoding =' ';
}
if (acceptEncoding.match(/\bdeflate\b/)) {
res.setHeader('Content-Encoding'.'deflate');
return zlib.createDeflate();
} else if (acceptEncoding.match(/\bgzip\b/)) {
res.setHeader('Content-Encoding'.'gzip');
return zlib.createGzip();
} else {
return null; }}Copy the code
The accept-encoding in the request header is used to determine whether and how to compress, and returns a stream object.
First write node static server (simple version) all code please see Github