前几天读到掘金大神一篇有深度的文章 为何视频网站的视频连接地址是blob?javascript
让kk对以前在业务上使用过的blob对象有了深刻的了解。因此有感而发,也想要整理一下在以前的场景下,如何运用blob对象对图片进行转码、上传等操做。html
kk查询了 MDN 对blob的定义:前端
Blob 对象表示一个不可变、原始数据的类文件对象。Blob 表示的不必定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。java
基于此定义的业务思考,kk可联想到 File对象 就是日常在form表单post形式上传图片文件的所对应的的形式。既然File基于blob,那么post方法上传一个blob对象也是可行的。web
如上图为身份证识别+视频活体检测在H5端实现的一个需求。在用户拍照上传身份证时候,前端要对该图片进行压缩、转码并上传,与此同时保证上传的性能和后端OCR算法的识别精度。算法
这里顺便想说起下:这个项目在刚开始的时候,上传的性能比较差,缘由是上传的图片为原图,容量很大致使上传时间太长,影响了用户体验。canvas
你们也知道,在微信里发送原图也是须要必定时间和资源的,在H5端也不例外。后端
通过反复试验,iOS的手机(kk试验的是iPhone 7 Plus 后置1200万像素),上传的原图容量为3M,换在最新发布的安卓手机上,状况就变得更加恶劣。设计师那台最新发布的荣耀V20,2000万像素,发送一张原图高达8M,这就尴尬了。这势必要在H5端上传以前对图片进行压缩。浏览器
要用到blob的地方就在于压缩事后,图片转码并上传的过程。事不宜迟,上代码。服务器
功能实现主要分为以下四大块
preview(event) {
var _this = this;
const eventPath = event.path || (event.composedPath && event.composedPath());
// 当选取一张图片时,新版iOS Safari浏览器会将图片存储在composedPath里,或者从composedPath方法获取,而普通浏览器依然会存放于path里
var domId = eventPath[0].id;
var cardType = _this.cardTypeJudge(eventPath[0].id);
let files = document.getElementById(eventPath[0].id).files[0];
if (files.size / 1048576 > 15) {
_this.confirmShow = true;
_this.confirmReason = '图片容量过大,请从新拍摄';
document.getElementById(domId).value = null
return false;
}
_this.smImage(files).then(resone => {
//上传图片
_this.uploadFile(resone, cardType).then(restwo => {
// console.log('restwo:' + restwo);
document.getElementById(eventPath[0].id).value = null;
_this.handleIdCardUpdateSuccess(_this.app.idCardFrontSrc, _this.app.idCardBackSrc, 1, cardType);
_this.submitBtnStatus();
});
});
},
复制代码
在用户从系统相册选定图片后,H5须要获取触发事件元素冒泡过程的全部元素,即点击选择的图片地址。
通过kk分析,在Chrome中能够经过event.path获取,Firefox和Safari中发现event并无path属性,确实须要调用event.composedPath()方法或者从event.composedPath取得。
这样才能保证H5在各移动端浏览器的功能兼容性。
Event.composedPath() 是浏览器一个新的标准,没想到移动端的更新步伐那么快。
以下是kk参考其余做者整理的获取图片元素地址的方法:
element.onClick(event) {
const ev = window.event || event;
const path = event.path || (event.composedPath && event.composedPath());
console.log(path) //[button#btn, div, body, html, document, Window]
}
复制代码
smImage(files) {
var _this = this;
return new Promise((resolve, reject) => {
// 压缩图片须要的一些元素和对象
var reader = new FileReader(),
//建立一个img对象
img = new Image();
// 缩放图片须要的canvas
var canvas = document.getElementById("imgcanvas");;
var context = canvas.getContext('2d');
// base64地址图片加载完毕后
img.onload = function () {
// 图片原始尺寸
var originWidth = this.width;
var originHeight = this.height;
// 最大尺寸限制,可经过国设置宽高来实现图片压缩程度
var maxWidth = 1300,
maxHeight = 1300;
// 目标尺寸
var targetWidth = originWidth,
targetHeight = originHeight;
// 图片尺寸超过1300x1300的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (originHeight / originWidth));
} else {
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (originWidth / originHeight));
}
}
// canvas对图片进行缩放
canvas.width = targetWidth;
canvas.height = targetHeight;
// 清除画布
context.clearRect(0, 0, targetWidth, targetHeight);
// 图片压缩
context.drawImage(img, 0, 0, targetWidth, targetHeight);
/*第一个参数是建立的img对象;第二个参数是左上角坐标,后面两个是画布区域宽高*/
//压缩后的图片base64 url
/*canvas.toDataURL(mimeType, qualityArgument),mimeType 默认值是'image/jpeg'; * qualityArgument表示导出的图片质量,只要导出为jpg和webp格式的时候此参数才有效果,默认值是0.92*/
var newUrl = canvas.toDataURL('image/jpeg', 0.5); //base64 格式
var blob = _this.convertBase64UrlToBlob(newUrl);
// console.log(blob);
// console.log('canvas宽度:' + targetWidth + ' 长度:' + targetHeight);
if (targetWidth < targetHeight) {
_this.isLongImg = true;
} else {
_this.isLongImg = false;
}
resolve(blob);
};
// 文件base64化,以便获知图片原始尺寸
img.src = _this.app.getFilePath(files);
})
}
复制代码
图片压缩整体采用canvas重绘的方法,对图片的尺寸和分辨率两方面压缩,其阈值经过canvas的各类API可实现人工调整。
以上所述,重绘大体分为两步:尺寸限定和压缩绘制。
先建立一个canvas画布,并限制其画布宽高。kk将画布宽maxWidth高maxHeight阈值都限定在了1300px,同时取得图片的原始宽originWidth高originHeight进行比较。
当width和height缩减到同比例下低于宽高限定阈值时,则根据画布宽高drawImage重绘。
主要取决于canvas.toDataURL() 方法中。当图片重绘至画布后,经过此方法可输出一个Data URI,即默认为png的base64串。
压缩的比例,图片输出的分辨率就取决于这个方法的第二个参数:encoderOptions 。
调整这个参数就能够导出不一样分辨率的base64串,kk在这里调整的是0.5,即50%分辨率的压缩。
上面kk也说起过,使用post方法上传文件,会取得一个File对象(基于blob),并做为二进制流(binary)上传到服务端。
如今重绘的图片呈现的形式是base64,则须要一个base64转blob的方法。
convertBase64UrlToBlob(urlData) {
var arr = urlData.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {
type: mime
});
}
复制代码
这个方法目前是怎么进行转码的kk还没弄清楚,只是做为Util工具类一直运用在该项目里,后期弄懂后会更新。
在此功能流程中,可调整的阈值有:
阈值调整后,性能相比于原来提高了50%,同时也保证了文字识别OCR的精度(实际上算法精度对图片分辨率的要求并不须要原图那么高,因此进行适当的压缩并不影响)。
性能提高50%体如今两方面:功能提高和体验提高。
原图容量由8M及以上压缩至2M如下,iOS原图更是压缩至1M如下,容量压缩了70%左右。
图片上传速度由原来的8.0s左右,提高至如今的2.0s~2.6s(3s之内,也取决于当时的网络情况和服务器的接口响应)。
这是我对这个项目开发经验的整理,以及blob对象运用的一些我的理解。望指正!