Maison >interface Web >js tutoriel >Implémentation du serveur de ressources statiques Node.js (avec code)
Le contenu de cet article concerne l'implémentation du serveur de ressources statiques de Node.js (avec code). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Cet article présente un exemple de projet simple de serveur de ressources statiques, dans l'espoir d'aider les débutants en Node.js. Le projet implique http, fs, url, path, zlib, process, child_process et d'autres modules, couvrant un grand nombre d'API couramment utilisées ; il comprend également la sélection de stratégies de mise en cache basées sur le protocole http, l'optimisation de la compression gzip, etc. ; le publiera sur npm et en fera un petit outil qui peut être installé et utilisé dans le monde entier. Bien que le moineau soit petit, il possède tous les organes internes. Cela ne vous rend-il pas un peu excité quand on y pense ? Sans plus tarder, passons au vif du sujet.
L'adresse du code source dans l'article se trouve dans l'annexe finale.
Vous pouvez d'abord expérimenter l'effet du projet :
Installation : npm i -g ici11
Entrez la commande à n'importe quelle adresse de dossier : ici
Parce que nous voulons publier sur npm, donc nous suivons d'abord les pratiques internationales, npm init, c'est parti ! Vous pouvez appuyer complètement sur Entrée sur la ligne de commande, et certaines configurations seront détaillées dans les étapes finales de publication.
La structure des répertoires est la suivante :
Le dossier bin stocke notre code d'exécution et Web est utilisé comme dossier de test contenant quelques pages Web. .
Serveur de ressources statiques, en termes simples, on saisit le formulaire « http://nom de domaine/test/ » dans le navigateur barre d'adresse index.html", le serveur trouve index.html dans le dossier correspondant dans le répertoire racine, lit le contenu du fichier et le renvoie au navigateur, et le navigateur le restitue à l'utilisateur.
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}`); });
Le code ci-dessus est notre code principal. Les fonctions principales ont été implémentées. Lors de l'exécution locale, vous pouvez voir que le répertoire du fichier a été renvoyé. Cliquez sur le nom du fichier pour parcourir les pages Web correspondantes. , photos et textes.
a été implémentée, mais nous pouvons optimiser certains aspects pour améliorer l'aspect pratique et apprendre plus d'API (faire semblant de compétences).
Notre opération actuelle de lecture des fichiers et de leur renvoi au navigateur consiste à les lire une fois et à les renvoyer tous en même temps via readFile. Cela peut certainement réaliser la fonction, mais. nous en avons une meilleure Méthode - Utiliser le flux pour effectuer des opérations d'E/S. Stream n'est pas un concept unique à node.js, mais la forme de fonctionnement la plus basique du système d'exploitation, donc théoriquement, tout langage côté serveur implémente l'API stream.
Pourquoi utiliser le flux est-il une meilleure façon ? Étant donné que la lecture et l'exploitation de fichiers volumineux en même temps consomment de la mémoire et du réseau, en particulier lorsque le nombre de visites d'utilisateurs est relativement important, à l'aide de flux, les données peuvent circuler et être exploitées petit à petit, améliorant ainsi les performances. La modification du code est la suivante :
if (stats.isFile()) { //是文件,返回文件内容 //在createServer时传入的回调函数被添加到了"request"事件上,回调函数的两个形参req和res //分别为http.IncomingMessage对象和http.ServerResponse对象 //并且它们都实现了流接口 let readStream = fs.createReadStream(_path); readStream.pipe(res); }
L'implémentation du codage est très simple. Lorsque nous devons renvoyer le contenu du fichier, nous créons un flux lisible et le dirigeons vers l'objet res.
L'amélioration des performances (expérience d'accès utilisateur) apportée par la compression gzip est très évidente, en particulier à l'ère actuelle où les applications de spa sont populaires, l'activation de la compression gzip peut réduire considérablement la taille des ressources de fichiers telles que js et css améliore la vitesse d'accès des utilisateurs. En tant que serveur de ressources statiques, nous devons bien sûr ajouter cette fonction.
Il existe un module zlib dans node, qui fournit de nombreuses API liées à la compression. Nous l'utilisons pour implémenter :
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); }
Avec l'expérience de l'utilisation de stream, regardons à nouveau ce paragraphe. C'est beaucoup plus facile à comprendre lors de l'écriture de code. Dirigez d'abord le flux de fichiers vers l'objet gzip, puis vers l'objet res. De plus, vous devez faire attention à une chose lorsque vous utilisez la compression gzip : vous devez définir le codage du contenu dans l'en-tête de réponse sur gzip. Sinon, le navigateur affichera un tas de caractères tronqués.
La mise en cache est quelque chose que les gens aiment et détestent, si elle est bien utilisée, elle peut améliorer l'expérience utilisateur et réduire la pression sur le serveur. des sortes de questions bizarres. De manière générale, le cache http du navigateur est divisé en cache fort (cache de non-vérification) et cache de négociation (cache de vérification).
Qu'est-ce que la mise en cache forte ? La mise en cache forte est contrôlée par deux champs d'en-tête, cache-control et expires. De nos jours, cache-control est généralement utilisé. Par exemple, nous définissons l'en-tête de réponse de cache-control : max-age=31536000, qui indique au navigateur que cette ressource a une période de cache d'un an. Dans un délai d'un an, il n'est pas nécessaire d'envoyer une requête au serveur et. la ressource est lue directement depuis le cache.
Le cache négocié utilise if-modified-since/last-modified, if-none-match/etag et d'autres champs d'en-tête, combinés avec le cache fort, si le cache fort n'atteint pas (ou indique au navigateur no-cache ), envoyer une requête au serveur, confirmer la validité de la ressource et décider de lire dans le cache ou de renvoyer une nouvelle ressource.
Avec les concepts ci-dessus, nous pouvons formuler notre stratégie de mise en 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}`); });
这里就简单的举个栗子啦,大家可以自由发挥!
虽然没太大卵用,但还是要加。我就是要让你们知道,我加完之后什么样,你们就是什么样 :-( 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}`); });
用process.cwd()代替__dirname。
我们最终要做成一个全局并且可以在任意目录下调用的命令,所以拼接path的代码修改如下:
//__dirname是当前文件的目录地址,process.cwd()返回的是脚本执行的路径 _path = path.join(process.cwd(), _path);
基本上我们的代码都写完了,可以考虑发布了!(不发布到npm上何以显示逼格?)
得到一个配置类似下面所示的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”这个名字可以随意起。
在index.js文件的开头加上:#!/usr/bin/env node
否则linux上运行会报错。
勉强贴一手命令,还不清楚自行百度:
没有账号的先添加一个,执行:
npm adduser
然后依次填入
Username: your name
Password: your password
Email: yourmail
npm会给你发一封验证邮件,记得点一下,不然会发布失败。
执行登录命令:
npm login
执行发布命令:
npm publish
发布的时候记得把项目名字、版本号、作者、仓库啥的改一下,别填成我的。
还有readme文件写一下,好歹告诉别人咋用,基本上和文首所说的用法是一样的。
好了,齐活。
还等啥啊,赶快把npm i -g xxx 这行命令发给你的小伙伴啊。什么?你没有小伙伴?告辞!
附
本文项目源码地址:https://github.com/gww666/here
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!