Maison  >  Article  >  interface Web  >  Comment implémenter un serveur statique à l'aide de Node.js

Comment implémenter un serveur statique à l'aide de Node.js

亚连
亚连original
2018-06-02 14:55:241385parcourir

Cet article présente principalement la méthode d'implémentation du serveur statique Node.js. Il est très bien et a une valeur de référence. Les amis dans le besoin peuvent s'y référer

Lorsque vous saisissez une URL, cette URL peut correspondre à le serveur. Une ressource (fichier) sur peut également correspondre à un répertoire. Le serveur analysera donc cette URL et fera différentes choses pour différentes situations. Si cette URL correspond à un fichier, le serveur renverra le fichier. Si cette URL correspond à un dossier, le serveur renverra une liste de tous les sous-fichiers/sous-dossiers contenus dans ce dossier. Ce qui précède correspond à ce que fait principalement un serveur statique.

Mais la situation réelle n'est pas aussi simple que cela. L'URL que nous obtenons peut être erronée, et le fichier ou le dossier auquel elle correspond peut ne pas exister du tout, ou certains fichiers et dossiers peuvent ne pas exister du tout. Ce qui est protégé par le système est caché et nous ne voulons pas que le client le sache. Par conséquent, nous devons effectuer des retours et des invites différents pour ces situations particulières.

De plus, avant de retourner effectivement un dossier, nous devons mener quelques négociations avec le client. Nous devons connaître les types de langage, les méthodes d'encodage, etc. que le client peut accepter afin d'effectuer différents traitements de retour pour différents navigateurs. Nous devons fournir au client des informations supplémentaires sur le fichier renvoyé afin que le client puisse mieux recevoir les données : le fichier doit-il être mis en cache et comment doit-il être mis en cache ? Le fichier est-il compressé ? Comment doit-il être décompressé ? Attendez...

À ce stade, nous avons une compréhension préliminaire de presque tout ce qu'un serveur statique fait principalement, c'est parti !

Mise en œuvre

Répertoire du projet

static-server/
|
| - bin/
| | - start # 批处理文件
|  
|
| - src/
| | - App.js # main文件
| | - Config.js # 默认配置
|
|
·- package.json

Fichier de configuration

Pour démarrer un serveur, nous devons connaître le numéro de port au démarrage du serveur et le répertoire de travail du serveur statique

let config = {
 host:'localhost' //提升用
 ,port:8080 //服务器启动时候的默认端口号
 ,path:path.resolve(__dirname,'..','test-dir') //静态服务器启动时默认的工作目录
}

Cadre général

Notez que

cela dans la fonction d'événement est par défaut l'objet lié (voici le petit server), Celui-ci est modifié dans le grand objet Server afin que les méthodes sous Server puissent être appelées dans la fonction de rappel.

class Server(){
 constructor(options){
  /* === 合并配置参数 === */
  this.config = Object.assign({},config,options)
 }
 start(){
  /* === 启动http服务 === */
  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){
  /* === 处理客户端请求,决定响应信息 === */
  // try
  //如果是文件夹 -> 显示子文件、文件夹列表
  //如果是文件 -> sendFile()
  // catch
  //出错 -> sendError()
 }
 sendFile(){
  //对要返回的文件进行预处理并发送文件
 }
 handleCache(){
  //获取和设置缓存相关信息
 }
 getEncoding(){
  //获取和设置编码相关信息
 }
 getStream(){
  //获取和设置分块传输相关信息
 }
 sendError(){
  //错误提示
 }
}
module.exports = Server;

traitement de la demande

Obtenir le chemin de l'URL, et le serveur local Splice l'adresse du répertoire racine de travail et renvoie un nom de fichier. Utilisez les méthodes filename et stat pour détecter s'il s'agit d'un fichier ou d'un dossier

. Utilisez la méthode readdir pour renvoyer la liste sous. le dossier et enveloppez la liste dans un objet. Le tableau est ensuite combiné avec le guidon pour compiler les données du tableau dans le modèle, et renvoie finalement le modèle au client

est un fichier et transmet la requête. res, statObj et filepath vers sendFile, qui est ensuite traité par sendFile

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)
   });
   // 让handlebar 拿着数去编译模板
   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);
 }
}

[astuce] Nous rendons la méthode de requête asynchrone, afin de pouvoir écrivez du code asynchrone tout comme nous écrivons du code synchrone

Méthode

sendFile

Implique la mise en cache, encodage, transmission segmentée et autres fonctions

sendFile(){
 if(this.handleCache(req,res,filepath,statObj)) return; //如果走缓存,则直接返回。
 res.setHeader('Content-type',mime.getType(filepath)+';charset=utf-8');
 let encoding = this.getEncoding(req,res); //获取浏览器能接收的编码并选择一种
 let rs = this.getStream(req,res,filepath,statObj); //支持断点续传
 if(encoding){
  rs.pipe(encoding).pipe(res);
 }else{
  rs.pipe(res);
 }
}

handleCache

Lors de la manipulation du cache, veuillez noter que le cache est divisé en cache forcé et cache comparatif, et la priorité du cache forcé est supérieure au cache relatif. Autrement dit, lorsque le cache forcé est en vigueur, le cache relatif ne sera pas utilisé et le serveur ne lancera pas de requête. Mais une fois le cache forcé échoué, le cache relatif sera utilisé si l'identifiant du fichier n'a pas changé, le cache relatif prendra effet et le client mettra toujours les données en cache pour obtenir les données, donc le cache forcé et le cache relatif. n'entrez pas en conflit. Lorsque la mise en cache forcée et la mise en cache relative sont utilisées ensemble, cela peut réduire la pression sur le serveur tout en gardant les données demandées à jour en temps opportun.

Une autre chose à noter est que si deux identifiants relatifs de fichier de cache sont définis en même temps, le cache ne prendra effet que si aucun d'eux n'a changé.

handleCache(req,res,filepath,statObj){
 let ifModifiedSince = req.headers['if-modified-since']; //第一次请求是不会有的
 let isNoneMatch = req.headers['is-none-match'];
 res.setHeader('Cache-Control','private,max-age=30');
 res.setHeader('Expires',new Date(Date.now()+30*1000).toGMTString()); //此时间必须为GMT
 
 let etag = statObj.size;
 let lastModified = statObj.ctime.toGMTString(); //此时间格式可配置
 res.setHeader('Etag',etag);
 res.setHeader('Last-Modified',lastModified);
 
 if(isNoneMatch && isNoneMatch != etag) return false; //若是第一次请求已经返回false
 if(ifModifiedSince && ifModifiedSince != lastModified) return false;
 if(isNoneMatch || ifModifiedSince){
 // 说明设置了isNoneMatch或则isModifiedSince且文件没有改变
  res.writeHead(304);
  res.end();
  return true;
 }esle{
  return false;
 }
}

getEncoding

Obtenir le type d'encodage que le navigateur peut recevoir à partir de l'en-tête de la requête et utiliser La correspondance régulière correspond à la première, crée l'instance zlib correspondante et la renvoie à la méthode sendFile afin qu'elle puisse être codée lors du renvoi du fichier.

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

transmission segmentée, principalement en utilisant le req.headers['range'] dans l'en-tête de la requête. Confirmez où. le fichier à recevoir commence et se termine. Cependant, les données réelles sont lues 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.match(/bytes = (\d*)-(\d*)/); //不可能有小数,网络传输的最小单位为一个字节
  if(result){
   start = isNaN(result[1])?0:parseInt(result[1]);
   end = isNaN(result[2])?end:parseInt(result[2])-1; //因为readstream的索引是包前又包后故要减去1
  }
 }
 return fs.createReadStream(filepath,{
  start,end
 });
}

est intégré dans un outil de ligne de commande

Nous pouvons utiliser le ligne de commande comme Enter npm start pour démarrer un dev-server et personnaliser une commande de démarrage pour démarrer notre serveur statique.

L'idée générale de l'implémentation est la suivante : configurer une commande de démarrage et le chemin du fichier qui exécute cette commande sous l'attribut bin dans packge.json. Ensuite, nous devons préparer un fichier batch, introduire notre fichier de serveur statique dans le fichier, laisser notre serveur fonctionner, puis lier ce fichier par nœud.

Ce qui précède est ce que j'ai compilé pour vous. J'espère que cela vous sera utile à l'avenir.

Articles associés :

Comment installer le chargeur style/css dans vue2.0

Diffusion d'événements Vue2.0 et Recevoir (mode observateur)

Tutoriel d'installation et d'utilisation du projet Vue internationalisation vue-i18n

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn