[目录]
问题来源
思路分析
前提的函数
将 file 文件转化为 base64
将 base64 转化为文件流
压缩方法
方法使用
问题总结
图片压缩程度
本来想做一个压缩图片的递归, 直到图片大小符合期望
iOS 拍摄方向问题
问题来源
这个问题, 源于上传图片文件的时候, 后台限制了 2MB 的大小, but 在调起相机拍照的时候分分钟超过了 2MB, 为了不影响用户体验和功能需求, 需要前端对大小进行压缩, 然后传到后台.
思路分析
找了很多资料, 发现只有 canvas 可以对图片进行压缩处理.
原理大概就是:
1, 先将图片的 file 文件转成 baseURL
2, 创建一个 image 标签去接收文件获取图片的宽高和比例.
3, 创建 canvas 画布设置画布的大小.
4, 将图片绘制到 canvas 上面.
5, 对 canvas 进行压缩处理, 获得新的 baseURL
6, 将 baseURL 转化回文件.
前提的函数
将 file 文件转化为 base64
- /**
- * @param {二进制文件流} file
- * @param {回调函数, 返回 base64} fn
- */
- function changeFileToBaseURL(file,fn){
- // 创建读取文件对象
- var fileReader = new FileReader();
- // 如果 file 没定义返回 null
- if(file == undefined) return fn(null);
- // 读取 file 文件, 得到的结果为 base64 位
- fileReader.readAsDataURL(file);
- fileReader.onload = function(){
- // 把读取到的 base64
- var imgBase64Data = this.result;
- fn(imgBase64Data);
- }
- }
将 base64 转化为文件流
- /**
- * 将 base64 转换为文件
- * @param {baseURL} dataurl
- * @param {文件名称} filename
- * @return {文件二进制流}
- */
- function dataURLtoFile(dataurl, filename) {
- var arr = dataurl.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 File([u8arr], filename, {type:mime});
- }
压缩方法
- /**
- * canvas 压缩图片
- * @param {参数 obj} param
- * @param {文件二进制流} param.file 必传
- * @param {目标压缩大小} param.targetSize 不传初始赋值 - 1
- * @param {输出图片宽度} param.width 不传初始赋值 - 1, 等比缩放不用传高度
- * @param {输出图片名称} param.fileName 不传初始赋值 image
- * @param {压缩图片程度} param.quality 不传初始赋值 0.92. 值范围 0~1
- * @param {回调函数} param.succ 必传
- */
- function pressImg(param){
- // 如果没有回调函数就不执行
- if(param && param.succ){
- // 如果 file 没定义返回 null
- if(param.file == undefined) return param.succ(null);
- // 给参数附初始值
- param.targetSize = param.hasOwnProperty("targetSize") ? param.targetSize : -1;
- param.width = param.hasOwnProperty("width") ? param.width : -1;
- param.fileName = param.hasOwnProperty("fileName") ? param.fileName: "image";
- param.quality = param.hasOwnProperty("quality") ? param.quality : 0.92;
- var _this = this;
- // 得到文件类型
- var fileType = param.file.type;
- // console.log(fileType) //image/jpeg
- if(fileType.indexOf("image") == -1){
- console.log('请选择图片文件 ^_^');
- return param.succ(null);
- }
- // 如果当前 size 比目标 size 小, 直接输出
- var size = param.file.size;
- if(param.targetSize> size){
- return param.succ(param.file);
- }
- // 读取 file 文件, 得到的结果为 base64 位
- changeFileToBaseURL(param.file,function(base64){
- if(base64){
- var image = new Image();
- image.src = base64;
- image.onload = function(){
- // 获得长宽比例
- var scale = this.width / this.height;
- // console.log(scale);
- // 创建一个 canvas
- var canvas = document.createElement('canvas');
- // 获取上下文
- var context = canvas.getContext('2d');
- // 获取压缩后的图片宽度, 如果 width 为 - 1, 默认原图宽度
- canvas.width = param.width == -1 ? this.width : param.width;
- // 获取压缩后的图片高度, 如果 width 为 - 1, 默认原图高度
- canvas.height = param.width == -1 ? this.height : parseInt(param.width / scale);
- // 把图片绘制到 canvas 上面
- context.drawImage(image, 0, 0, canvas.width, canvas.height);
- // 压缩图片, 获取到新的 base64Url
- var newImageData = canvas.toDataURL(fileType,param.quality);
- // 将 base64 转化成文件流
- var resultFile = dataURLtoFile(newImageData,param.fileName);
- // 判断如果 targetSize 有限制且压缩后的图片大小比目标大小大, 就弹出错误
- if(param.targetSize != -1 && param.targetSize <resultFile.size){
- console.log("图片上传尺寸太大, 请重新上传 ^_^");
- param.succ(null);
- }else{
- // 返回文件流
- param.succ(resultFile);
- }
- }
- }
- });
- }
- }
方法使用
文件的 size 是按照字节, 所以我们需要把要求的大小转化成字节.
1 字节就是 1byte 就是 1B,1KB = 1024B,1MB = 1024 * 1024B
- <input type="file" id="fileImg" class="fileImg"/>
- // 图片文件上传获取 url
- $("#fileImg").on('change',function(){
- pressImg({
- file:this.files[0],
- targetSize:2 * 1024 * 1024,
- quality:0.5,
- width:600,
- succ:function(resultFile){
- // 如果不是 null 就是压缩成功
- if(resultFile){
- //TODO
- }
- }
- })
- });
问题总结
图片压缩程度
图片的压缩程度不太好确定, 所以可以进行多次尝试, 根据需求方的要求进行调整.
改变目标图片的大小和清晰度都可以改变图片的压缩程度.
本来想做一个压缩图片的递归, 直到图片大小符合期望
后来发现
如果目标大小比较小, 图片如何进行压缩都不能满足条件的时候, 会造成循环无法跳出, 浪费资源.
如果图片进行几次压缩之后, 文件大小不会改变了, 有的时候还会增加, 奇奇怪怪.
所以就放弃了递归.
iOS 拍摄方向问题
因为 iOS 调起系统相机拍照是逆时针旋转了 90 度.
而在我压缩图片之后传到后台, 发现图片的 exif 信息的拍摄方向丢失, 导致 iOS 上传的图片都是逆时针旋转了 90 度. 这个问题安卓不曾发现.
image.PNG
目前有些怀疑, 是 base64 转化成 file 文件的时候, 丢失的.
之后验证后会在这里进行补充说明.
@version1.0--2019-8-2-- 创建《使用 canvas 压缩图片大小》
©burning_韵七七
来源: http://www.jianshu.com/p/f46195810c3b