Home > Web Front-end > JS Tutorial > Node.js static resource server implementation (with code)

Node.js static resource server implementation (with code)

不言
Release: 2019-03-08 17:14:54
forward
2486 people have browsed it

The content of this article is about the static resource server implementation of Node.js (with code). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

This article introduces a simple static resource server example project, hoping to help Node.js beginners. The project involves http, fs, url, path, zlib, process, child_process and other modules, covering a large number of commonly used APIs; it also includes caching strategy selection based on the http protocol, gzip compression optimization, etc.; eventually we will publish it on npm and make it A small tool that can be installed and used globally. Although the sparrow is small, it has all the internal organs. Doesn’t it make you a little excited when you think about it? Without further ado, let’s get down to speed.

The source code address in the article is in the final appendix.
You can experience the project effect first:
Installation: npm i -g here11
Enter the command at any folder address: here

step1 Create a new project

Because we want to publish to npm, so we first follow international practice, npm init, let’s go! You can press Enter all the way on the command line, and some configurations will be detailed in the final publishing steps.

The directory structure is as follows:

Node.js static resource server implementation (with code)

The bin folder stores our execution code, and web is used as a test folder with some web pages in it.

step2 code

step2.1 Prototype

Static resource server, in layman’s terms, we enter something like “http://domain name/test/” in the browser address bar index.html", the server finds index.html from the corresponding folder in the root directory, reads the file content and returns it to the browser, and the browser renders it to the user.

const http = require("http");
const url = require("url");
const fs = require("fs");
const path = require("path");

const item = (name, parentPath) => {
    let path = parentPath = `${parentPath}/${name}`.slice(1);
    return `<p><a>${name}</a></p>`;
}

const list = (arr, parentPath) => {
    return arr.map(name => item(name, parentPath)).join("");
}

const server = http.createServer((req, res) => {
    let _path = url.parse(req.url).pathname;//去掉search
    let parentPath = _path;
    _path = path.join(__dirname, _path);
    try {
        //拿到路径所对应的文件描述对象
        let stats = fs.statSync(_path);
        if (stats.isFile()) {
            //是文件,返回文件内容
            let file = fs.readFileSync(_path);
            res.end(file);
        } else if (stats.isDirectory()) {
            //是目录,返回目录列表,让用户可以继续点击
            let dirArray = fs.readdirSync(_path);
            res.end(list(dirArray, parentPath));
        } else {
            res.end();
        }
    } catch (err) {
        res.writeHead(404, "Not Found");
        res.end();
    }
});

const port = 2234;
const hostname = "127.0.0.1";
server.listen(port, hostname, () => {
    console.log(`server is running on http://${hostname}:${port}`);
});
Copy after login

The above code is our core code. The core functions have been implemented. If you run it locally, you can see that the file directory has been returned. Click on the file name to browse the corresponding web pages, pictures, and texts.

step2.2 Optimization

The function has been implemented, but we can optimize it in some aspects to improve practicality and learn more APIs (pretend skills).

1. stream

Our current operation of reading files and returning them to the browser is to read them out once and return them all at once through readFile. This can certainly achieve the function, but we have a better solution. Method - Use stream to perform IO operations. Stream is not a concept unique to node.js, but the most basic form of operation of the operating system, so theoretically, any server-side language implements the stream API.

Why is using stream a better way? Because reading and operating large files at one time consumes memory and the network, especially when the number of user visits is relatively large; with the help of streams, data can flow and be operated bit by bit, thereby improving performance. The code modification is as follows:

if (stats.isFile()) {
    //是文件,返回文件内容
    //在createServer时传入的回调函数被添加到了"request"事件上,回调函数的两个形参req和res
    //分别为http.IncomingMessage对象和http.ServerResponse对象
    //并且它们都实现了流接口
    let readStream = fs.createReadStream(_path);
    readStream.pipe(res);
}
Copy after login

The coding implementation is very simple. When we need to return the file content, we create a readable stream and direct it to the res object.

2. gzip compression

The performance (user access experience) improvement brought by gzip compression is very obvious, especially in the current era of popular spa applications, turning on gzip compression can greatly reduce the The size of file resources such as js and css improves user access speed. As a static resource server, of course we have to add this function.

There is a zlib module in node, which provides a lot of compression-related APIs. We use it to implement:

const zlib = require("zlib");

if (stats.isFile()) {
    //是文件,返回文件内容

    res.setHeader("content-encoding", "gzip");
    
    const gzip = zlib.createGzip();
    let readStream = fs.createReadStream(_path);
    readStream.pipe(gzip).pipe(res);
}
Copy after login

With the experience of using stream, let’s look at this code again It will be easier to understand then. Direct the file stream to the gzip object first, and then to the res object. In addition, you need to pay attention to one thing when using gzip compression: you need to set the content-encoding in the response header to gzip. Otherwise, the browser will display a bunch of garbled characters.

3. http cache

Cache is something that people love and hate. If used well, it can improve the user experience and reduce server pressure; if used poorly, you may face various problems. All kinds of weird questions. Generally speaking, browser http cache is divided into strong cache (non-verification cache) and negotiation cache (verification cache).

What is strong caching? Strong caching is controlled by two header fields, cache-control and expires. Nowadays, cache-control is generally used. For example, we set the response header of cache-control: max-age=31536000, which tells the browser that this resource has a one-year cache period. Within one year, there is no need to send a request to the server and the resource is read directly from the cache.

The negotiated cache uses if-modified-since/last-modified, if-none-match/etag and other header fields, combined with strong cache, if the strong cache does not hit (or tells the browser no-cache ), send a request to the server, confirm the validity of the resource, and decide to read from the cache or return a new resource.

With the above concepts, we can formulate our caching strategy:

if (stats.isFile()) {
    //是文件,返回文件内容
    
    //增加判断文件是否有改动,没有改动返回304的逻辑
    
    //从请求头获取modified时间
    let IfModifiedSince = req.headers["if-modified-since"];
    //获取文件的修改日期——时间戳格式
    let mtime = stats.mtime;
    //如果服务器上的文件修改时间小于等于请求头携带的修改时间,则认定文件没有变化
    if (IfModifiedSince && mtime <p>这样一套缓存策略在现代前端项目体系下还是比较合适的,尤其是对于spa应用来讲。我们希望index.html能够保证每次向服务器验证是否有更新,而其余的文件统一本地缓存一个月(自己定);通过webpack打包或其他工程化方式构建之后,js、css内容如果发生变化,文件名相应更新,index.html插入的manifest(或script链接、link链接等)清单会更新,保证用户能够实时得到最新的资源。</p><p>当然,缓存之路千万条,适合业务才重要,大家可以灵活制定。</p><h4>4. 命令行参数</h4><p>作为一个在命令行执行的工具,怎么能不象征性的支持几个参数呢?</p><pre class="brush:php;toolbar:false">const config = {
    //从命令行中获取端口号,如果未设置采用默认
    port: process.argv[2] || 2234,
    hostname: "127.0.0.1"
}
server.listen(config.port, config.hostname, () => {
    console.log(`server is running on http://${config.hostname}:${config.port}`);
});
Copy after login

这里就简单的举个栗子啦,大家可以自由发挥!

5. 自动打开浏览器

虽然没太大卵用,但还是要加。我就是要让你们知道,我加完之后什么样,你们就是什么样 :-( duang~

const exec = require("child_process").exec;
server.listen(config.port, config.hostname, () => {
    console.log(`server is running on http://${config.hostname}:${config.port}`);
    exec(`open http://${config.hostname}:${config.port}`);
});
Copy after login

6. process.cwd()

用process.cwd()代替__dirname。
我们最终要做成一个全局并且可以在任意目录下调用的命令,所以拼接path的代码修改如下:

//__dirname是当前文件的目录地址,process.cwd()返回的是脚本执行的路径
_path = path.join(process.cwd(), _path);
Copy after login

step3 发布

基本上我们的代码都写完了,可以考虑发布了!(不发布到npm上何以显示逼格?)

step3.1 package.json

得到一个配置类似下面所示的json文件:

{
    "name": "here11",
    "version": "0.0.13",
    "private": false,
    "description": "a node static assets server",
    "bin": {
        "here": "./bin/index.js"
    },
    "repository": {
        "type": "git",
        "url": "https://github.com/gww666/here.git"
    },
    "scripts": {
        "test": "node bin/index.js"
    },
    "keywords": [
        "node"
    ],
    "author": "gw666",
    "license": "ISC"
}
Copy after login

其中bin和private较为重要,其余的按照自己的项目情况填写。
bin这个配置代表的是npm i -g xxx之后,我们运行here命令所执行的文件,“here”这个名字可以随意起。

step3.2 声明脚本执行类型

在index.js文件的开头加上:#!/usr/bin/env node
否则linux上运行会报错。

step3.3 注册npm账号

勉强贴一手命令,还不清楚自行百度:

没有账号的先添加一个,执行:
npm adduser

然后依次填入
Username: your name
Password: your password
Email: yourmail

npm会给你发一封验证邮件,记得点一下,不然会发布失败。

执行登录命令:
npm login

执行发布命令:
npm publish

发布的时候记得把项目名字、版本号、作者、仓库啥的改一下,别填成我的。
还有readme文件写一下,好歹告诉别人咋用,基本上和文首所说的用法是一样的。

好了,齐活。

step3.4

还等啥啊,赶快把npm i -g xxx 这行命令发给你的小伙伴啊。什么?你没有小伙伴?告辞!

本文项目源码地址:https://github.com/gww666/here

The above is the detailed content of Node.js static resource server implementation (with code). For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template