欢迎访问我的个人网站:https://coderyuan.com
文章目录
背景
之前在开发实验室官网(https://www.xiyoumobile.com)的时候,由于图片特别多,学校服务器走电信和教育网,带宽也不够,在某些网络环境图片加载十分缓慢,而且有时候主页打开需要10s+的时间,所以考虑从图片压缩上节省网络带宽
目前业界的图片压缩方案有以下几种,他们的压缩比都很高,都可以有效减小图片文件的体积
方案 | 提出者 | 编码类型 | 开源 | 官网 | 浏览器支持情况 |
---|---|---|---|---|---|
WebP | VP8 | 是 | https://developers.google.com/speed/webp/ | Chrome、Opera、Edge、Android,及其他基于Chromium内核的浏览器 | |
BPG | Fabrice Bellard | HEVC | 是 | http://bellard.org/bpg | 无 |
TPG | 腾讯 | AVS2(类似AVC) | 否 | 未知 | QQ浏览器 |
JPEG XR | 微软 | 未知 | 否 | (微软已投奔WebP) | IE、Edge |
根据TPG推出时腾讯的微信公众号文章来看,TPG的压缩算法相比WebP而言,能够使体积变得更小,图片更清晰,但是适用范围还是太局限了,所以还是选择了WebP
但主要问题是前端代码中已经固定了某些静态图片资源的URL及扩展名,数据库中保存的HTML中的图片引用也是如此,即使把图片全部转换为WebP格式,如何保证不改动HTML及CSS代码,就能够对支持的浏览器返回WebP格式图片,而不支持的浏览器返回JPG/PNG?
于是经过一段时间摸索和整理,就有了现在用在我个人网站的coderyuan-image-server:https://github.com/yuanguozheng/coderyuan-image-server
基本原理及流程
判断浏览器是否支持WebP
首先引用一段来自w3的定义(RFC 2616):https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
The Accept request-header field can be used to specify certain media types which are acceptable for the response. Accept headers can be used to indicate that the request is specifically limited to a small set of desired types, as in the case of a request for an in-line image.
Accept = "Accept" ":"
#( media-range [ accept-params ] )
media-range = ( "*/*"
| ( type "/" "*" )
| ( type "/" subtype )
) *( ";" parameter )
accept-params = ";" "q" "=" qvalue *( accept-extension )
accept-extension = ";" token [ "=" ( token | quoted-string ) ]
这里定义了HTTP请求头中Accept字段的含义——表示Response可以接受的媒体类型(MIME-Type)
如果支持WebP,那么Accept中将会包含image/webp
这样的字符串
于是乎,我们就可以根据浏览器请求图片资源时的Accept字段来区分是否真正支持WebP格式
为什么不使用User-Agent?
UA一般用于区分浏览器类别、操作系统、网络状况等,通过这些字段区分是否支持WebP,是不可靠的。
举个例子,Edge浏览器第一版是不支持WebP的,现在及以后的版本就会支持了,如果通过UA来区分,成本就会十分高昂
完整的图片请求流程
图片上传
为了能够支持根据Accept
自动返回对应的图片,以达到节省网络带宽,在图片采集上也得进行定制,即:在上传原图(如:PNG、JPG)的同时,还应对图片进行转码,生成对应的WebP文件,并按照一定的规则重命名,以便Image Server查找
如:上传文件为:image.png
,保存这个图片的同时,应当再转码一个WebP格式文件,保存为:image.webp
或者image.png.webp
那么在请求这个文件的时候,附加一个.webp
的扩展名即可
技术框架
由于比较熟悉Node.js,而且它的异步非阻塞I/O能够在有限的资源下,带来良好的性能,所以我在这里选择了Node.js来开发图片服务器(Image Server),其中包括图片上传、转换和路径解析、图片传输模块。当然,选择其他的语言及相关的框架也是完全没有问题的,比如:Java、Go
express
做过Node.js开发的人应该都不陌生,可谓是Node里最著名的Web框架,使用它来构建一个Web服务,并处理HTTP请求的Header、参数(包括上传文件)、路由等十分方便
Github:https://github.com/expressjs/express/
node-static
node-static用于做静态文件服务器,主要用于根据请求的URL、Accept等参数,返回给客户端(浏览器)所对应的图片,从而实现同一URL,Chrome返回WebP,而Safari/FireFox返回PNG的效果
Github:https://github.com/cloudhead/node-static
为什么不使用Express自带的serve-static?
-
查阅node-static的源码,发现它支持的功能更适用,如:断点续传、gzip压缩等
-
没有对Express模块的依赖,如果做单独使用,功能支持的比serve-static更好
-
性能更好一些
实现
图片解析及传输服务
图片传输服务主要用于向服务器发送请求的客户端传输其能够支持的图片文件流,所以需要实现请求路径的解析和实际文件的匹配,最后将文件流封装在HTTP响应报文当中
这里只需要引入Node自带的url
和path
即可完成对请求URL的解析,非常方便
代码如下:
const URL = require('url');
const path = require('path');
const url = URL.parse(req.url);
const pathInfo = path.parse(url.pathname);
由path.parse
解析所得到的对象继承自ParsedPath
类,包含5个字段,root
、dir
、base
、ext
、name
,其解释如下
/**
* A parsed path object generated by path.parse() or consumed by path.format().
*/
export interface