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