WdBly Blog

懂事、有趣、保持理智

WdBly Blog

懂事、有趣、保持理智

周维 | Jim

603927378@qq.com

全站webp支持

全站webp图片切换

需求调研,需将网站的图片切换到webp格式,用户提示网站加载速度,节省服务端流量。

七牛cdn图片处理

?imageView2/format/webp

问题1

这样处理后的在支持webp的浏览器和客户端能正常显示图片,但在不支持的终端如safair则不能正常显示图片。兼容性问题也是很多网站没有上webp的原因。

那么针对这个兼容性的问题,也是有渐进增强的方案,普通适配版浏览器我们采用常规图片,支持webp的浏览器使用增强方案,使用webp格式图片。

var is_support_webp = document.createElement("canvas").toDataURL("image/webp").indexOf("data:image/webp") === 0; var format = is_support_webp ? "webp" : "jpg"; var img_url = "https://cdn.wddsss.com/img/a.jpg?imageView2/format/"+ format;

对于这种处理方式,比较简单高效。思考,在七牛内部是如何实现format的呢?

1.快速转换功能,七牛只存储了原图,对于不同的后缀图临时转换返回。
2.对于转换后的图片缓存,第二次获取时从缓存(或直接存储起来,但没有使用用户自己的空间)

问题2

使用七牛的jpg->webp的同时不能使用修改图片质量的操作
image.png

转换算法

js实现

function conversion() { var imgFile = new FileReader(); var file = document.getElementById("inputimg").files[0]; imgFile.readAsDataURL(file); imgFile.onload = function() { var img = new Image(); img.src = this.result; img.onload=function(){ var _this = this; var canvas = document.createElement("canvas"); canvas.width = _this.width; canvas.height = _this.height; // drawImage(obj, x, y, w, h) canvas.getContext("2d").drawImage(_this, 0, 0); // canvas.toDataURL(type, encoderOptions); // 将画布输出为base64图片 var src = canvas.toDataURL("image/webp", 0.5); console.log(src); }; }; }

思路, 1:通过file input获取上传的图片源文件, 2:通过FileReader的readAsDataURL方法获取到图片的base64值, 3:通过new Image()并传入base64对象生成图片对象, 4:通过canvas的drawImage方法将image转化为canvas对象, 5:通过canvas的toDataURL将canvas输出为不同类型(质量)的base64图片值。

转化销量如何: 原图越大转化时间越长, 经测试200-400k的原图转化时间大约在100-200毫秒,3M以上的图转化时间大于1s,且原图的格式,转化后的图片质量都会影响转化时间。

node实现

//使用webp-converter库操作图片。 var converter= require('webp-converter'); converter.cwebp("input.jpg","output.webp","-q 85",function(code,err){ console.log(status, error); });

cwebp

node简易图片服务器使用思路,当用户上传图片时,调用cwebp方法将图片另存一份服务output.webp, 当用户请求图片时,根据用户request的accept字段匹配是否支持webp格式图片,若是支持则返回webp格式的图片给用户,意思是不管客户端请求的图片是什么类型,统一通过accept来返回图片。

/image\/webp/gi.test(requestAccept);

服务端缓存问题

对于图片类的服务端缓存问题主要是服务端渲染的场景,访问一个链接,返回对应文档,同时传统的缓存服务器(如squid)会采用键值对的方式对最终文档以字符串的方式缓存,下一次请求相同资源若是匹配则会命中缓存。

// 使用node memory-cache中间件模拟缓存 var mcache = require('memory-cache'); var cache = (duration) => { return (req, res, next) => { let key = '__express__' + req.originalUrl || req.url let cachedBody = mcache.get(key) // 缓存命中 if (cachedBody) { res.send(cachedBody) return } else { // 未命中 res.sendResponse = res.send res.send = (body) => { mcache.put(key, body, duration * 1000); res.sendResponse(body) } next() } } }

如果是使用上述缓存机制,在不同的终端上会有问题,如文档中的webp格式图片在safari中无法正常显示,压缩类型(Content-Encoding)为gzip的文档无法在低版本IE中无法使用,或者其他条件。

通过设置response header中的vary字段来标识缓存,来解决上述问题。vary可以设置的值一般有accept,accept-encoding,accept-language,user-agent等。当缓存服务器接收到请求后,会根据vary的值去获取request中对应的字段,并按照一定的规则生成一个hash值,最终的缓存命中需要hash值和key都命中才会返回对应的文档。当然若是没有命中缓存,则需要根据request header中的信息返回可用的文档给用户,同时生成hash值key和value。

image.png

image.png

总结

在网站中支持webp的方式总结大概有如下几种方式。

cdn

在客户端判断支持性,然后使用不同的format值。优点方便快捷,缺点不支持图片质量压缩

js转化

通过canvas的toDataURL达到图片格式转换的目的,优点不需要请求额外接口,自立更生适用于少量一次性格式转换,如在线jpg转webp等。缺点也是异常明显,转换速度慢,不能缓存每次都需要运行转换程序,不能适用于大量图片转换。

服务端判断

思路:使用node脚本在服务端对网站的图片进行转换,同时长效存储,在有图片请求时判断accept,然后返回可用图片。

服务端缓存

使用代理服务器缓存时,针对支持的浏览器,存贮不同的图片格式地址。