Node static file server detailed

Source: Internet
Author: User
Support Features:

    1. Reading static files

    2. Access to the directory can automatically look for the following index.html file, if not index.html lists the file list

    3. MIME type support

    4. Cache Support/Control

    5. Supports gzip compression

    6. Range support, breakpoint continuation

    7. Global command execution

    8. Child process Run

This article mainly and you introduced the actual combat node static file Server example, I hope to help everyone.

1. Create a service to read static files

First introduce the HTTP module, create a server, and listen to the configuration port:


Const HTTP = require (' http ');  Const SERVER = Http.createserver ();  Listener Request Server.on (' request ', Request.bind (this));  Server.listen (Config.port, () = {  Console.log (' Static file service started successfully, Access Localhost:${config.port} ');

Write an FN to handle the request, return the static file, the URL module gets the path:


Const URL = require (' URL '); Const FS = require (' FS '); function request (req, res) {const {Pathname} = Url.parse (Req.url);//access path  Const filepath = path.join (Config.root, pathname); File path  fs.createreadstream (filepath). pipe (res);//Read file and respond}

Support to find index.html:


if (pathname = = =/') {  const RootPath = path.join (config.root, ' index.html ');  try{   Const INDEXSTAT = Fs.statsync (rootpath);   if (indexstat) {    filepath = RootPath;   }}  catch (e) {     }}

When accessing the directory, list the file directories:


Fs.stat (filepath, (err, stats) = {if (err) {  res.end (' not found ');  Return } if (Stats.isdirectory ()) {let  files = Fs.readdirsync (filepath);  Files = Files.map (File = = ({   name:file,   url:path.join (pathname, file)  });  Let HTML = this.list () ({   title:pathname,   files  });  Res.setheader (' Content-type ', ' text/html ');  Res.end (HTML); } }

HTML templates:


Function List () {let  Tmpl = Fs.readfilesync (Path.resolve (__dirname, ' template ', ' list.html '), ' UTF8 ');  Return Handlebars.compile (Tmpl); }


<! DOCTYPE html> 

2.MIME Type Support

Use the MIME module to get the file type and set the encoding:


Res.setheader (' Content-type ', Mime.gettype (filepath) + '; Charset=utf-8 ');

3. Cache support

HTTP protocol cache:

cache-control:http1.1 content that tells the client how to cache data, as well as rules

    1. Private Clients can cache

    2. Public client and proxy server can be cached

    3. MAX-AGE=60 cache content will expire in 60 seconds

    4. No-cache needs to validate data using contrast cache, forcing re-authentication to the source server

    5. No-store All content is not cached, forcing cache and contrast cache are not triggered

expires:http1.0 content, Cache-control will overwrite, tell the client when the cache expires

ETag: Hash value of content the next time a client request adds a If-none-match:etag value in the request header

Last-modified: Last modification time the next client request adds a If-modified-since:last-modified value to the request


 Handlecache (req, res, stats, hash) {//When the resource expires, the client discovers the last request resource, the server has a send last-modified, and then requests it again with If-modified-since Const IFMODI fiedsince = req.headers[' if-modified-since ']; The server sends the ETag, and the client requests it again with the If-none-match field to ask if it expires const Ifnonematch = req.headers[' If-none-match ']; http1.1 content max-age=30 for forced cache for 30 seconds and 30 seconds to request again in cache private client-side cache only, proxy server is not cacheable res.setheader (' Cache-control ', ' private,max-age =30 '); http1.0 content is consistent with Cache-control tell the client what time, resource expiration priority is lower than Cache-control res.setheader (' Expires ', New Date (Date.now () + 30 * 100 0). toGMTString ()); Sets the hash res.setheader (' etag ', hash) generated by the ETag based on the content; Set last-modified file Last modified time const LASTMODIFIED = stats.ctime.toGMTString ();  Res.setheader (' last-modified ', lastmodified); Determine if the etag expires if (ifnonematch && ifnonematch! = hash) {return false;}//Determine the last modified time of the file if (ifmodifiedsince &AMP;&A mp Ifmodifiedsince! = lastmodified) {return false;}//If present and equal, go cache 304 if (Ifnonematch | | ifmodifiedsince) {Res.writehead  (304);  Res.end (); return true; } else {returnFalse } }

4. Compression

The client sends the content, through the request head Accept-encoding:gzip, deflate tells the server to support which compression format, the server compresses the content according to the supported compression format. If the server is not supported, it is not compressed.


GetEncoding (req, res) {  Const acceptencoding = req.headers[' accept-encoding '];  Gzip and deflate Compression  if (/\bgzip\b/.test (acceptencoding)) {   res.setheader (' content-encoding ', ' gzip ');   return Zlib.creategzip ();  } else if (/\bdeflate\b/.test (acceptencoding)) {   res.setheader (' content-encoding ', ' deflate ');   return Zlib.createdeflate ();  } else {   return null;}  }

5. Continuation of the breakpoint

The server range:bytes=0-xxx in the request header to determine whether to do a range request, if the value exists and is valid, only send back the requested portion of the file content, the response status code becomes 206, indicating partial Content, and set the Content-range. If not, returns a 416 status code indicating that the request Range is not satisfiable. If you do not include a request header for range, you continue to respond in a regular manner.


GetStream (req, res, filepath, statobj) {let  start = 0;  Let end = Statobj.size-1;  Const RANGE = req.headers[' range '];  if (range) {   res.setheader (' accept-range ', ' bytes ');   Res.statuscode = 206;//Returns the entire contents of a block 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  });}

6. Global command execution

With NPM link implementation

    1. Create a soft link for the NPM package catalog and chain it to {prefix}/lib/node_modules/

    2. Create a soft link to the executable (bin) and chain it to {prefix}/bin/{name}

The NPM link command implements the global executable of the NPM Package command via the linked directory and executable file.

Package.json inside Configuration


{bin: {"hope-server": "Bin/hope"}}

Create a bin directory hope file below the project, using Yargs to configure command-line arguments


Tell the computer to run my files with node #! /USR/BIN/ENV node  const YARGS = require (' Yargs '); const INIT = require ('.. /src/index.js '); Const ARGV = yargs.option (' d ', {alias: ' Root ', demand: ' false ', type: ' String ', DEFAULT:PROCESS.CWD (), Description: ' Static text The root directory '}). Option (' O ', {alias: ' Host ', demand: ' false ', default: ' localhost ', type: ' String ', Description: ' Configure listening host '}). opt Ion (' P ', {alias: ' Port ', demand: ' false ', type: ' Number ', default:8080, Description: ' Configure Port Number '}). Option (' C ', {alias: ' ch Ild ', demand: ' false ', type: ' Boolean ', Default:false, Description: ' Whether the child process is running '} '. Usage (' hope-server [Options] '). Example (' hope-server-d/-P 9090-o localhost ', ' listening for client requests on the 9090 port on this machine '). Help (' H '). argv;  Start Service init (argv);

7. Child process Run

Implemented through Spawn

Index.js


const {Spawn} = require (' child_process '); Const SERVER = require ('./hope ');  function init (argv) {  //if configured as a child process to turn on the service if  (argv.child) {   ///Sub-process Start service   Const child = spawn (' node ', [' Hope.js ', Json.stringify (argv)], {    CWD: __dirname,    detached:true,    stdio: ' Inherit '   });    Background run   child.unref ();   Exit the main thread and let the child threads run   process.exit (0) separately;  } else {   const SERVER = new server (argv);   Server.start ();  } }  module.exports = Init;hope.js if (process.argv[2] && process.argv[2].startswith (' {')) {const ARGV = JSON. Parse (process.argv[2]); Const SERVER = new Hope (argv); Server.start (); }

8. Source code and testing

Source Address: Hope-server


NPM Install Hope-server-g

Go to any directory


Hope-server

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.