>웹 프론트엔드 >JS 튜토리얼 >노드 정적 파일 서버 사용에 대한 자세한 설명

노드 정적 파일 서버 사용에 대한 자세한 설명

php中世界最好的语言
php中世界最好的语言원래의
2018-03-29 09:18:211522검색

이번에는 노드 정적 파일 서버 사용에 대한 자세한 설명을 가져왔습니다. 노드 정적 파일 서버 사용 시 주의 사항은 무엇인가요? 실제 사례를 살펴보겠습니다.

이 글에서는 실제 노드 정적 파일 서버의 예시를 주로 소개하고, 자세한 내용은 다음과 같습니다.

지원 기능:

  1. 정적 파일 읽기

  2. 디렉토리에 액세스하면 자동으로 찾을 수 있습니다. index.html 파일 다음에 index.html이 없으면 파일 목록을 나열

  3. MIME 유형 지원

  4. 캐시 지원/제어

  5. gzip 압축 지원

  6. 범위 지원, 중단점 재개

  7. 전역 명령 실행

  8. 하위 프로세스 실행

1. 정적 파일을 읽는 서비스 만들기

먼저 http 모듈을 소개하고, 서버를 만들고, 구성된 포트를 수신합니다.

 const http = require('http');
 
 const server = http.createServer();
 
 // 监听请求
 server.on('request', request.bind(this));
 
 server.listen(config.port, () => {
  console.log(`静态文件服务启动成功, 访问localhost:${config.port}`);
 });

Write 특히 요청을 처리하고 정적 파일을 반환하기 위한 fn, url 모듈은 다음 경로를 얻습니다.

 const url = require('url');
 const fs = require('fs');
 function request(req, res) {
 const { pathname } = url.parse(req.url); // 访问路径
 
 const filepath = path.join(config.root, pathname); // 文件路径
 
 fs.createReadStream(filepath).pipe(res); // 读取文件,并响应
 }

index.html 검색 지원:

 if (pathname === '/') {
  const rootPath = path.join(config.root, 'index.html');
  try{
   const indexStat = fs.statSync(rootPath);
   if (indexStat) {
    filepath = rootPath;
   }
  } catch(e) {
   
  }
 }

디렉토리에 액세스할 때 파일 디렉터리 나열:

 fs.stat(filepath, (err, stats) => {
 if (err) {
  res.end('not found');
  return;
 }
 if (stats.isDirectory()) {
  let files = fs.readdirSync(filepath);
  files = files.map(file => ({
   name: file,
   url: path.join(pathname, file)
  }));
  let html = this.list()({
   title: pathname,
   files
  });
  res.setHeader('Content-Type', 'text/html');
  res.end(html);
 }
 }

html 템플릿:

 function list() {
  let tmpl = fs.readFileSync(path.resolve(dirname, 'template', 'list.html'), 'utf8');
  return handlebars.compile(tmpl);
 }
 <!DOCTYPE html>
 <html lang="en">
 <head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>{{title}}</title>
 </head>
 <body>
 <h1>hope-server静态文件服务器</h1>
 <ul>
  {{#each files}}
  <li>
   <a href={{url}}>{{name}}</a>
  </li>
  {{/each}}
 </ul>
 </body>
 </html>

2. MIME 유형 지원

mime 모듈을 사용하여 파일 유형을 가져오고 인코딩을 설정합니다:

res.setHeader('Content-Type', mime.getType(filepath) + ';charset=utf-8');

3. 캐시 지원

http 프로토콜 캐시:

Cache-Control: http1.1 콘텐츠, 클라이언트에게 방법을 알려줍니다. 데이터를 캐시하고 규칙

  1. 개인 클라이언트는

  2. 공용을 캐시할 수 있습니다. 클라이언트와 프록시 서버 모두 캐시할 수 있습니다

  3. max-age=60 캐시된 콘텐츠는 60초 후에 만료됩니다

  4. no-cache 데이터를 검증하려면 비교 캐시를 사용해야 하며 원본 서버에서 강제로 다시 검증해야 합니다

  5. no-store 모든 콘텐츠가 캐시되지 않으며 강제 캐싱이나 비교 캐싱이 실행되지 않습니다

Expires: http1.0 콘텐츠, 캐시 제어는 이를 무시하고 캐시가 만료되는 시기를 클라이언트에 알려줍니다.

ETag: 콘텐츠 다음 클라이언트 요청의 해시 값이 요청 헤더에 추가됩니다. if-none-match: etag value

Last-Modified: 마지막 수정 시간 if-modified-since: Last-Modified Value

 handleCache(req, res, stats, hash) {
 // 当资源过期时, 客户端发现上一次请求资源,服务器有发送Last-Modified, 则再次请求时带上if-modified-since
 const ifModifiedSince = req.headers['if-modified-since'];
 // 服务器发送了etag,客户端再次请求时用If-None-Match字段来询问是否过期
 const ifNoneMatch = req.headers['if-none-match'];
 // http1.1内容 max-age=30 为强行缓存30秒 30秒内再次请求则用缓存 private 仅客户端缓存,代理服务器不可缓存
 res.setHeader('Cache-Control', 'private,max-age=30');
 // http1.0内容 作用与Cache-Control一致 告诉客户端什么时间,资源过期 优先级低于Cache-Control
 res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toGMTString());
 // 设置ETag 根据内容生成的hash
 res.setHeader('ETag', hash);
 // 设置Last-Modified 文件最后修改时间
 const lastModified = stats.ctime.toGMTString();
 res.setHeader('Last-Modified', lastModified);
 
 // 判断ETag是否过期
 if (ifNoneMatch && ifNoneMatch != hash) {
  return false;
 }
 // 判断文件最后修改时间
 if (ifModifiedSince && ifModifiedSince != lastModified) {
  return false;
 }
 // 如果存在且相等,走缓存304
 if (ifNoneMatch || ifModifiedSince) {
  res.writeHead(304);
  res.end();
  return true;
 } else {
  return false;
 }
 }

4. Compression

클라이언트는 콘텐츠를 전송하고 서버에 어떤 압축 형식을 알려줍니다. 요청 헤더 Accept-Encoding: gzip, deflate를 통해 지원됩니다. 서버는 지원되는 압축 형식을 기반으로 콘텐츠를 압축합니다. 서버에서 지원하지 않으면 압축이 수행되지 않습니다.

 getEncoding(req, res) {
  const acceptEncoding = req.headers['accept-encoding'];
  // gzip和deflate压缩
  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;
  }
 }

5. 재개 가능한 업로드

서버는 요청 헤더의 Range: bytes=0-xxx를 사용하여 Range 요청을 하고 있는지 확인합니다. 이 값이 존재하고 유효한 경우 파일 콘텐츠의 요청된 부분만 사용됩니다. 응답의 상태 코드가 206으로 변경되어 부분 콘텐츠 및 콘텐츠 범위가 설정되었음을 나타냅니다. 유효하지 않은 경우 요청 범위가 만족스럽지 않음을 나타내는 416 상태 코드가 반환됩니다. Range 요청 헤더가 포함되지 않은 경우 계속해서 일반적인 방식으로 응답합니다.

 getStream(req, res, filepath, statObj) {
  let start = 0;
  let end = statObj.size - 1;
  const 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]) ? start : parseInt(result[1]);
    end = isNaN(result[2]) ? end : parseInt(result[2]) - 1;
   }
  }
  return fs.createReadStream(filepath, {
   start, end
  });
 }

6. 전역 명령 실행

npm 링크를 통해 달성

  1. npm 패키지 디렉터리에 대한 소프트 링크를 생성하고 이를 {prefix}/lib/node_modules/

  2. 실행 파일 생성(bin ) 소프트 링크, {prefix}/bin/{name}

에 연결 npm link 명령은 디렉터리와 실행 파일을 연결하여 npm 패키지 명령을 전역적으로 실행 가능하게 만듭니다.

package.json의 구성

 {
 bin: {
 "hope-server": "bin/hope"
 }
 }

프로젝트 아래에 bin 디렉터리 희망 파일을 생성하고 yargs를 사용하여 매개변수를 전달하도록 명령줄을 구성합니다

 // 告诉电脑用node运行我的文件
 #! /usr/bin/env node
 
 const yargs = require('yargs');
 const init = require('../src/index.js');
 const argv = yargs.option('d', {
 alias: 'root',
 demand: 'false',
 type: 'string',
 default: process.cwd(),
 description: '静态文件根目录'
 }).option('o', {
 alias: 'host',
 demand: 'false',
 default: 'localhost',
 type: 'string',
 description: '配置监听的主机'
 }).option('p', {
 alias: 'port',
 demand: 'false',
 type: 'number',
 default: 8080,
 description: '配置端口号'
 }).option('c', {
 alias: 'child',
 demand: 'false',
 type: 'boolean',
 default: false,
 description: '是否子进程运行'
 })
 .usage('hope-server [options]')
 .example(
 'hope-server -d / -p 9090 -o localhost', '在本机的9090端口上监听客户端的请求'
 ).help('h').argv;
 
 // 启动服务
 init(argv);

7 실행 중인 하위 프로세스

spawn

index.js

 const { spawn } = require('child_process');
 const Server = require('./hope');
 
 function init(argv) {
  // 如果配置为子进程开启服务
  if (argv.child) {
   //子进程启动服务
   const child = spawn('node', ['hope.js', JSON.stringify(argv)], {
    cwd: dirname,
    detached: true,
    stdio: 'inherit'
   });
 
   //后台运行
   child.unref();
   //退出主线程,让子线程单独运行
   process.exit(0);
  } else {
   const server = new Server(argv);
   server.start();
  }
 }
 
 module.exports = init;
hope.js
 if (process.argv[2] && process.argv[2].startsWith('{')) {
 const argv = JSON.parse(process.argv[2]);
 const server = new Hope(argv);
 server.start();
 }

8. 소스 코드 및 테스트

소스 코드 주소: hope-server

npm install hope-server -g

아무 디렉토리나 입력하세요

hope-server

이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 더 흥미로운 정보를 보려면 다른 관련 항목에 주목하세요. PHP 중국어 웹사이트의 기사!

추천 도서:


을 사용하여 Vue의 전역 구성 요소를 등록하는 방법

선택 옵션 오버레이 문제

위 내용은 노드 정적 파일 서버 사용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.