Node. js static server implementation method, node. js static Server

Source: Internet
Author: User
Tags getstream sendfile

Node. js static server implementation method, node. js static Server

When you enter a url, the url may correspond to a resource (File) on the server or a directory. The So server analyzes the url and does different things for different situations. If the url corresponds to a file, the server returns the file. If the url corresponds to a folder, the server returns a list Of all subfolders in the folder. The above is the main task of a static server.

However, the actual situation is not as simple as this. The url we get may be incorrect. The corresponding file or folder may not exist at all, or some files and folders are hidden by the system, and we do not want to let the client know. Therefore, we need to make some different responses and prompts for these special situations.

Furthermore, before we actually return a file, we need to negotiate with the client. We need to know the language type and encoding method that the client can accept so as to perform different return processing for different browsers. We need to tell the client some additional information about the returned file so that the client can better receive data: Does the file need to be cached? Is the file compressed? How can I decompress it? And so on...

Now, we have a preliminary understanding of almost all the things a static server is doing. let's go!

Implementation

Project directory

Static-server/|-bin/|-start # batch file |-src/|-App. js # main file |-Config. js # Default Configuration |-package. json

Configuration File

To start a server, we need to know the port number when the server is started and the working directory of the static server.

Let config = {host: 'localhost' // upgrade, port: 8080 // default port number when the server is started, path: path. resolve (_ dirname ,'.. ', 'test-dir') // default working directory when the static server is started}

Overall framework

Note:

By default, this in the event function points to the bound object (here it is a small server). this is changed to the large object of Server, so that the method under Server can be called in the callback function.

Class Server () {constructor (options) {/* = merging configuration parameters = */this. config = Object. assign ({}, config, options)} start () {/* === start http service ===*/let server = http. createServer (); server. on ('request', this. request. bind (this); server. listen (this. config. port, () => {let url = '$ {this. config. host }:$ {this. config. port} '; console. log ('server started at $ {chalk. green (url)} ')} async request (req, res) {/* === process client requests, decides the response information ===* // try // if it is a folder-> show the sub-file, folder list // if it is a file-> sendFile () // catch // error-> sendError ()} sendFile () {// preprocessing the file to be returned and sending the file} handleCache () {// obtain and set cache-related information} getEncoding () {// obtain and set encoding-related information} getStream () {// obtain and set Block Transmission-related information} sendError () {// error message} module. exports = Server;

Request Processing

Obtain the pathname of the url and splice it with the local working root directory address of the server. A filename is returned, and the filename and stat methods are used to check whether a file or a folder is used.

Is a folder. The readdir method is used to return the list under the folder, package the list into an array composed of objects, compile the array data into the template with handlebar, and finally return the template to the client.

Yes file. Pass req, res, statObj, and filepath to sendFile, and then submit it to sendFile for processing.

Async request (req, res) {let pathname = url. parse (req. url); if (pathname = '/favicon. ico ') return; let filepath = path. join (this. config. root, pathname); try {let statObj = await stat (filepath); if (statObj. isDirectory () {let files = awaity readdir (filepath); files. map (file => {name: file, path: path. join (pathname, file)}); // let handlebar compile the template with the number. let html = this. list ({title: pathname, files}) res. setHeader ('content-type', 'text/html '); res. end (html);} else {this. sendFile (req, res, filepath, statObj) ;}} catch (e) {this. sendError (e, req, res );}}

[Tip] We will async the request method, so that we can write Asynchronization like writing Synchronous Code.

Method

SendFile

Caching, encoding, and multipart Transmission

SendFile () {if (this. handleCache (req, res, filepath, statObj) return; // if it is cached, it is returned directly. Res. setHeader ('content-type', mime. getType (filepath) + '; charset = UTF-8'); let encoding = this. getEncoding (req, res); // get the encoding that the browser can receive and select a let rs = this. getStream (req, res, filepath, statObj); // supports resumable if (encoding) {rs. pipe (encoding ). pipe (res);} else {rs. pipe (res );}}

HandleCache

During cache processing, cache is classified into forced cache and comparison cache, and the priority of forced cache is higher than that of relative cache. That is to say, when the forced cache takes effect, it does not follow the relative cache and does not initiate a request like a server. However, once the forced cache fails, the relative cache will be used. If the file ID does not change, the relative cache will take effect, and the client will still cache the data to retrieve the data, therefore, the forced cache does not conflict with the relative cache. When forced caching and relative caching are used together, colleagues who can reduce the pressure on the server can keep request data updated in a timely manner.

In addition, if two relative cached file IDs are set at the same time, the cache takes effect only when both of them are not changed.

HandleCache (req, res, filepath, statObj) {let ifModifiedSince = req. headers ['if-modified-since ']; // The first request does not have let isNoneMatch = req. headers ['is-none-Match']; res. setHeader ('cache-control', 'Private, max-age = 30'); res. setHeader ('expires', new Date (Date. now () plus 30*1000 ). toGMTString (); // this time must be GMT let etag = statObj. size; let lastModified = statObj. ctime. toGMTString (); // This time format can be configured with res. setHeader ('etag', Etag ); Res. setHeader ('Last-modified', lastModified); if (isNoneMatch & isNoneMatch! = Etag) return false; // if the first request has returned false if (ifModifiedSince & ifModifiedSince! = LastModified) return false; if (isNoneMatch | ifModifiedSince) {// indicates that isNoneMatch is set or isModifiedSince is set, and the file does not change res. writehead( 304); res. end (); return true;} esle {return false ;}}

GetEncoding

Obtain the encoding type that the browser can receive from the request header, use regular expression matching to match the first one, and create a corresponding zlib instance and return it to the sendFile method, to encode the returned file.

getEncoding(req,res){ let acceptEncoding = req.headers['accept-encoding']; 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; }}

GetStream

Multipart transmission mainly usesreq.headers['range']To confirm the start and end of the file to be received.fs.createReadStream .

GetStream (req, res, filepath, statObj) {let start = 0; let end = startObj. size-1; let range = req. headers ['range']; if (range) {res. setHeader ('Accept-range', 'bytes '); res. statusCode = 206; // let result = range of the returned data. match (/bytes = (\ d *)-(\ d *)/); // The minimum unit of network transmission is one byte if (result) {start = isNaN (result [1])? 0: parseInt (result [1]); end = isNaN (result [2])? End: parseInt (result [2])-1; // because the readstream index is before and after the package, the value of 1} return fs is subtracted. createReadStream (filepath, {start, end });}

Packaged as a command line tool

We can enter npm start Startdev-serverYou can also customize a startup command to start our static server.

The general idea is to configure a startup command and the path of the file to execute this command under the bin attribute in packge. json. Then we need to prepare a batch processing file, introduce our static Server File into the file, let our server run up and then the file node link.

Summary

The above is the implementation method of Node. js static server introduced by the editor. I hope it will be helpful to you. If you have any questions, please leave a message and I will reply to you in a timely manner. Thank you very much for your support for the help House website!

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.