我们目前读取文件返回给浏览器的操作是通过readFile一次性读出来,一次性返回,这样当然可以实现功能,但我们有更好的方式——用stream(流)进行IO操作。stream并不是node.js独有的概念,而是操作系统最基本的一种操作形式,所以理论上讲,任何一门server端语言都实现了stream的API。
为什么讲用stream是一种更好的方式?因为一次性读取、操作大文件,内存和网络是吃不消的,尤其在用户访问量比较大的情况下更为明显;而借助stream可以让数据流动起来,一点一点操作,从而提升性能。代码修改如下:
- if (stats.isFile()) {
- //是文件,返回文件内容
- //在createServer时传入的回调函数被添加到了"request"事件上,回调函数的两个形参req和res
- //分别为http.IncomingMessage对象和http.ServerResponse对象
- //并且它们都实现了流接口
- let readStream = fs.createReadStream(_path);
- readStream.pipe(res);
- }
编码实现非常简单,在需要返回文件内容时,我们创建了一个可读流,并把它直接导向了res对象。
2. gzip压缩
gzip压缩带来的性能(用户访问体验)提升是非常明显的,尤其在当下spa应用大行其道的时代,开启gzip压缩,可以大幅减小js、css等文件资源的体积,提升用户访问速度。作为一个静态资源服务器,我们当然要加上这个功能。
node中有一个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);
- }
有了stream的使用经验,我们再看这段代码的时候就好理解多了。把文件流先导向gzip对象,再导向res对象。此外,使用gzip压缩的时候还需要注意一点:需要把响应头里的content-encoding设置为gzip。否则浏览器会把一堆乱码展示出来。
3. http缓存
缓存这个东西让人又爱又恨,用得好,可以提升用户体验,减轻服务器压力;用得不好,可能就会面临各种各样奇奇怪怪的问题。一般来讲浏览器http缓存分为强缓存(非验证性缓存)和协商缓存(验证性缓存)。
什么叫强缓存呢?强缓存是由cache-control和expires两个首部字段控制的,,现在一般用cache-control。比如我们设置了cache-control: max-age=31536000的响应头,就是告诉浏览器这个资源有一年的缓存期,一年内不用向服务端发送请求,直接从缓存中读取资源。
而协商性缓存是使用if-modified-since/last-modified、if-none-match/etag等首部字段,配合强缓存,在强缓存没有命中(或告知浏览器no-cache)的时候,向服务器发送请求,确认资源的有效性,决定从缓存中读取或是返回新的资源。
有了以上概念,我们便可以制定我们的缓存策略:
- if (stats.isFile()) {
- //是文件,返回文件内容
- //增加判断文件是否有改动,没有改动返回304的逻辑
- //从请求头获取modified时间
- let IfModifiedSince = req.headers["if-modified-since"];
- //获取文件的修改日期——时间戳格式
- let mtime = stats.mtime;
- //如果服务器上的文件修改时间小于等于请求头携带的修改时间,则认定文件没有变化
- if (IfModifiedSince && mtime <= new Date(IfModifiedSince).getTime()) {
- //返回304
- res.writeHead(304, "not modify");
- return res.end();
- }
- //第一次请求或文件被修改后,返回给客户端新的修改时间
- res.setHeader("last-modified", new Date(mtime).toString());
- res.setHeader("content-encoding", "gzip");
- let reg = /\.html$/;
- //不同的文件类型设置不同的cache-control
- if (reg.test(_path)) {
- //我们对html文件执行每次必须向服务器验证资源有效性的策略
- res.setHeader("cache-control", "no-cache");
- } else {
- //我们对其余的静态资源文件采取强缓存策略,一个月内无需向服务器索取
- res.setHeader("cache-control", `max-age=${1 * 60 * 60 * 24 * 30}`);
- }
- //执行gzip压缩
- const gzip = zlib.createGzip();
- let readStream = fs.createReadStream(_path);
- readStream.pipe(gzip).pipe(res);
- }
(编辑:ASP站长网)
|