当前位置: 首页 > 后端技术 > Node.js

Node搭建静态文件服务器

时间:2023-04-04 00:52:41 Node.js

静态文件服务器实现读取静态文件的功能MIME类型支持缓存支持/控制支持gzip压缩Range支持,断点续传发布为可执行命令,可以在后台运行,可以使用npminstall-ginstallation首先建立工程目录,工程目录如下:project|---bin命令行执行放置脚本||---公共静态文件服务器默认静态文件夹||---src相关代码实现功能|||__template模板文件夹|||__app.js主函数??文件(主文件)||__config.js配置文件||---package.josn(初始化)要启动一个服务器,我们需要知道服务器启动时的端口号,在config.js中配置:letconfig={host:'localhost'//用于提示,端口:8080//服务器启动时的默认端口号path:path.resolve(__dirname,'..','test-dir')//静态服务器启动时的默认工作目录}在读取静态文件之前,必须先启动server,然后所有的方法都在类Server的方法中//handlebar编译模板,得到一个渲染方法,然后传入实际数据,得到渲染后的HTML函数list(){lettmpl=fs.readFileSync(path.resolve(__dirname,'template','list.html'),'utf8');returnhandlebars.compile(tmpl);//编译最后渲染}classServer{constructor(argv){this.list=列表();this.config=Object.assign({},this.config,argv);}start(){letserver=http.createServer();//创建服务器//当客户端向服务器发送数据时,会触发请求事件server.on('request',this.request.bind(this));server.listen(this.config.port,()=>{//监听端口号leturl=`http://${this.config.host}:${this.config.port}`;debug(`服务器启动于${chalk.green(url)}`);});}//发送错误信息,sendError(err,req,res){res.statusCode=500;res.end(`${err.toString()}`);}}module.exports=Server;看静态文件设计思路的时候,第一次输入一个url的时候,可能对应服务器上的一个文件,或者对应一个目录,看是文件还是目录。如果文件不存在,则返回404状态码,并将未找到页面发送给客户端。如果文件存在:打开文件读取并设置响应头。将文件发送给客户端。如果是目录,则打开Directorylistasyncrequest(req,res){//先获取客户端想要的文件或文件夹路径let{pathname}=url.parse(req.url);//获取文件informationofthepathletfilepath=path.join(this.config.root,pathname);//服务器上对应的服务器物理路径try{letstatObj=awaitstat(filepath);//获取路径的文件信息if(statObj.isDirectory()){//如果是目录,应该显示目录下的文件列表letfiles=awaitreaddir(filepath);//读取文件列表files=files.map(file=>({//把每个字符串变成一个对象名:file,url:path.join(路径名,文件)}));//handlebar编译模板lethtml=this.list({title:pathname,files});res.setHeader('Content-Type','text/html');设置请求头res.end(html);}else{this.sendFile(req,res,filepath,statObj);//读取文件}}catch(e){//没有访问就发送错误信息debug(inspect(e));//检查将对象转换为字符this.sendError(e,req,res);}}缓存支持/控制设计思路缓存分为强制缓存和比较缓存:两种缓存规则可以同时强制缓存的优先级高于比较缓存。也就是说当执行强制缓存规则时,如果缓存生效,则直接使用缓存,不再执行比较缓存规则。如果强制缓存生效,则不需要与服务器通信。交互,比对缓存是否有效,需要和服务器进行交互。第一次访问服务器时,服务器返回资源的标识和缓存,客户端会在本地缓存数据库中缓存资源。当客户端需要这个数据的时候,需要获取缓存ID,然后向服务器询问我的资源是否是最新的。如果是最新的,则直接使用缓存数据。如果不是最新的,服务器会返回新的资源和缓存规则,客户端会根据缓存规则缓存新的数据。通过最后修改时间判断缓存是否可用Last-Modified:响应时告诉客户端这个资源的最后修改时间If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源有Last-Modified语句,再次向服务器请求时带上headerIf-Modified-Since。服务器收到请求后,找到头部If-Modified-Since,将其与请求资源的最后修改时间进行比较。如果上次修改时间较新,说明该资源又被修改过,响应最新的资源内容,返回200状态码;如果上次修改时间与If-Modified-Since相同,表示资源没有被修改,则响应304表示没有更新。告诉浏览器继续使用保存的缓存文件。ETag是一个资源标签。如果资源不改变,它就不会改变。1、如果客户端要判断缓存是否可用,可以先获取缓存中文档的ETag,然后通过If-None-Match向web服务器发送请求,询问缓存是否可用。2服务器收到请求后,将服务器中这个文件的ETag与请求头中的If-None-Match进行比较。如果值相同,说明缓存还是最新的,web服务器会发送一个304NotModified响应码给客户端,表示缓存没有被修改,可以使用。3.如果没有,web服务器将最新版本的文档发送给浏览器客户端handleCache(req,res,filepath,statObj){让isNoneMatch=req.headers['is-none-match'];res.setHeader('Cache-Control','private,max-age=30');//max-age=30缓存内容将在30秒后过期res.setHeader('Expires',newDate(Date.now()+30*1000).toGMTString());让etag=statObj.size;让lastModified=statObj.ctime.toGMTString();res.setHeader('ETag',etag);//获取ETagres.setHeader('Last-Modified',lastModified);//服务器文件的最后修改时间//如果任何一个比较缓存头不匹配,则缓存不会走if(isNoneMatch&&isNoneMatch!=etag){//缓存过期returnfasle;}if(ifModifiedSince&&ifModifiedSince!=lastModified){//缓存过期returnfasle;}//当请求中有比较缓存头时,返回304,否则不去缓存if(isNoneMatch||ifModifiedSince){//缓存有效res.writeHead(304);重发();返回真;}else{返回错误;}}支持gzip压缩设计思路浏览器会自带支持的压缩类型,最常用的两种是gzip和放气根据请求头Accept-Encoding,返回不同的压缩格式。getEncoding(req,res){letacceptEncoding=req.headers['accept-encoding'];//获取客户端发送的压缩请求头信息if(/\bgzip\b/.test(acceptEncoding)){//如果是gzip格式res.setHeader('Content-Encoding','gzip');返回zlib.createGzip();}elseif(/\bdeflate\b/.test(acceptEncoding)){//如果是deflate格式res.setHeader('Content-Encoding','deflate');返回zlib.createDeflate();}else{returnnull;//不压缩}}范围支持,断点续传的设计思想该选项指定了下载字节的范围,常用于分块下载文件。服务器告诉客户端它可以使用范围response.setHeader('Accept-Ranges','bytes')。Range:bytes=0-xxx判断是否为Range请求。如果该值存在且有效,则只返回请求的部分文件内容,响应的状态码变为206,如果无效,则返回416状态码。表示RequestRangeNotSatisfiablegetStream(req,res,filepath,statObj){letstart=0;//可读流的开始位置letend=statObj.size-1;/可读流的结束位置letrange=req.headers['range'];//获取客户端的range请求头信息,if(range){//断点续传res.setHeader('Accept-Range','bytes');res.statusCode=206;//Returnapieceoftheentirecontentletresult=range.match(/bytes=(\d*)-(\d*)/);//续传的分段内容不能有小数,网络最小单位传输是一个字节if(result){start=isNaN(result[1])?开始:parseInt(结果[1]);结束=isNaN(结果[2])?结束:parseInt(结果[2])-1;}}returnfs.createReadStream(filepath,{start,end});}发布为可执行命令首先在package.json中配置"bin":{"http-static":"bin/www"}#!/usr/bin/envnode//这段代码一定要写在开头,为了兼容各种电脑平台的差异//-d--root静态文件目录-o--hosthost-p--port端口号letyargs=require('yargs');letServer=require('../src/app.js');letargv=yargs.option('d',{alias:'root',demand:'false',type:'string',default:process.cwd(),description:'静态文件和目录'}).option('o',{alias:'host',demand:'localhost',type:'string',description:'请配置监听主机'}).option('p',{alias:'root',demand:'false',type:'number',default:8080,description:'请配置端口号'}).usage('http-static[options]').example('http-staticc-d/8080-olocalhost','在本机9090端口监听客户端请求').help('h').argv;//argv={d,root,o,host,p,port}letserver=newServer(argv);//启动服务server.start();这样就可以直接在命令行输入http-static来启动静态文件服务器,然后实现命令行调用的功能,最后使用npmpublish发布到npm,我们就可以全局安装了通过npminstall-g。参考资料Node.js静态文件服务器实战