ホームページ  >  記事  >  ウェブフロントエンド  >  Node.js 静的リソース サーバーの実装 (コード付き)

Node.js 静的リソース サーバーの実装 (コード付き)

不言
不言転載
2019-03-08 17:14:542450ブラウズ

この記事の内容は、Node.js の静的リソース サーバーの実装 (コード付き) に関するもので、一定の参考価値があります。必要な友人は参考にしていただければ幸いです。

この記事では、Node.js の初心者を支援することを目的として、単純な静的リソース サーバーのサンプル プロジェクトを紹介します。このプロジェクトには、http、fs、url、path、zlib、process、child_process およびその他のモジュールが含まれており、一般的に使用される多数の API をカバーしています。また、http プロトコルに基づくキャッシュ戦略の選択、gzip 圧縮の最適化なども含まれています。これを npm で公開し、グローバルにインストールして使用できる小さなツールにします。スズメは小さいのに内臓がしっかりしていて、想像するとちょっとワクワクしませんか?早速、本題に入りましょう。

記事内のソース コードのアドレスは最後の付録にあります。
最初にプロジェクトの効果を体験できます:
インストール: npm i -g here11
任意のフォルダー アドレスにコマンドを入力します: ここに

step1 新しいプロジェクトを作成します

npm に公開したいので、まず国際的な慣例に従い、npm init を実行します。コマンド ラインで Enter キーを押し続けると、一部の構成については最後の公開手順で詳しく説明されます。

ディレクトリ構造は次のとおりです:

Node.js 静的リソース サーバーの実装 (コード付き)

bin フォルダーには実行コードが保存され、web はいくつかの Web ページを含むテスト フォルダーとして使用されます。 。

step2 コード

step2.1 プロトタイプ

静的リソース サーバー、わかりやすく言うと、ブラウザに「http://ドメイン名/test/」のようなものを入力します。アドレス バーindex.html」の場合、サーバーはルート ディレクトリ内の対応するフォルダーからindex.htmlを検索し、ファイルの内容を読み取ってブラウザに返し、ブラウザはそれをユーザーに表示します。

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}`);
});

上記のコードはコア コードです。コア機能が実装されています。ローカルで実行すると、ファイル ディレクトリが返されたことがわかります。ファイル名をクリックして、対応する Web ページを参照してください。 、写真、テキスト。

step2.2 最適化

機能は実装されていますが、実用性を向上させ、より多くの API (ふりスキル) を学習するために、いくつかの点で最適化することができます。

1. stream

ファイルを読み込んでブラウザに返す現在の操作は、readFile で一度読み出して一括で返すというもので、確かに機能は実現できますが、方法 - ストリームを使用して IO 操作を実行します。ストリームは、node.js に固​​有の概念ではありませんが、オペレーティング システムの最も基本的な操作形式であるため、理論的には、どのサーバー側言語もストリーム API を実装します。

なぜストリームを使用する方が良い方法なのでしょうか?一度に大きなファイルを読み取って操作すると、特にユーザーのアクセス数が比較的多い場合にメモリとネットワークが消費されるため、ストリームを利用することでデータを少しずつ流して操作できるため、パフォーマンスが向上します。コードの変更は次のとおりです:

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

コーディングの実装は非常に単純で、ファイルのコンテンツを返す必要がある場合は、読み取り可能なストリームを作成し、それを res オブジェクトに送ります。

2. gzip 圧縮

gzip 圧縮によってもたらされるパフォーマンス (ユーザー アクセス エクスペリエンス) の向上は非常に明白で、特にスパ アプリケーションが人気の現在の時代では、gzip 圧縮をオンにすると、 jsやcssなどのファイルリソースのサイズにより、ユーザーのアクセス速度が向上します。静的リソースサーバーなので、当然この機能を追加する必要があります。

ノードには zlib モジュールがあり、多くの圧縮関連 API を提供します。これを使用して実装します:

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);
}

ストリームの使用経験を踏まえて、このコードをもう一度見てみましょうそうすればもっと分かりやすくなるでしょう。ファイル ストリームを最初に gzip オブジェクトに送信し、次に res オブジェクトに送信します。さらに、gzip 圧縮を使用する場合は、1 つのことに注意する必要があります。応答ヘッダーのコンテンツ エンコーディングを gzip に設定する必要があります。そうしないと、ブラウザで大量の文字化けが表示されます。

3. http キャッシュ

キャッシュは人によって好き嫌いが分かれるもので、うまく使えばユーザー エクスペリエンスが向上し、サーバーの負荷が軽減されますが、使い方が悪いとさまざまな問題に直面する可能性があります。あらゆる種類の奇妙な質問。一般的に、ブラウザの http キャッシュは、強力なキャッシュ (非検証キャッシュ) とネゴシエーション キャッシュ (検証キャッシュ) に分けられます。

強力なキャッシュとは何ですか?強力なキャッシュは、cache-control と Expires の 2 つのヘッダー フィールドによって制御されますが、現在では、cache-control が一般的に使用されます。たとえば、cache-control の応答ヘッダー max-age=31536000 を設定すると、このリソースのキャッシュ期間は 1 年間であることがブラウザに通知されます。1 年以内は、サーバーにリクエストを送信する必要がなく、リソースはキャッシュから直接読み取られます。

ネゴシエートされたキャッシュは、強力なキャッシュがヒットしない場合 (またはブラウザにノーを指示した場合)、強力なキャッシュと組み合わせて、if-modified-since/last-modified、if-none-match/etag およびその他のヘッダー フィールドを使用します。 -cache )、サーバーにリクエストを送信し、リソースの有効性を確認し、キャッシュから読み取るか新しいリソースを返すかを決定します。

上記の概念を使用して、キャッシュ戦略を策定できます。

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}`);
});

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

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}`);
});

6. process.cwd()

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

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

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"
}

其中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

以上がNode.js 静的リソース サーバーの実装 (コード付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。