JS对图片进行base64压缩以及图片的EXIF-Orientation信息
来源:互联网 发布:直线制职能制矩阵制 编辑:程序博客网 时间:2024/04/30 22:53
最近在调试一个bug,项目刚接手几天,刚开始还一脸懵逼的,后来对这个bug的解决思路渐渐轻车熟路,好了,废话不说。。。
项目中使用了一个叫 localResizeIMG 前端控件进行图片的选择,加载,base64压缩,再而上传到后台服务器。
在处理bug的过程中,学习了两个知识: 1.js对图片进行base64压缩; 2.关于图片中的EXIF中的Orientation(方向)信息。
下面,分别用于记录所学到的两个知识:
1.js对图片进行base64压缩
网上查阅了一些资料,基本的思路如下:
1.收到传入的文件后,创建一个 canvas 对象 和 blob 对象(其实也就是对应的文件引用,所以能被 img src直接引用)
2.创建 img 对象,标记允许跨域处理,src 设置为 blob,接下来就是开始压缩了。
3.开始压缩,获取图片旋转的方向(EXIF中的Orientation信息),计算用户设置的尺寸,设置canvas,然后压缩成base64
以下摘取localResizeIMG的一段代码进行剖析
function Lrz (file, opts) { var that = this; if (!file) throw new Error('没有收到图片,可能的解决方案:https://github.com/think2011/localResizeIMG/issues/7'); opts = opts || {}; that.defaults = { width : null, height : null, fieldName: 'file', quality : 0.7 }; that.file = file; for (var p in opts) { //配置参数 if (!opts.hasOwnProperty(p)) continue; that.defaults[p] = opts[p]; } return this.init(); //初始化}Lrz.prototype.init = function () { var that = this, file = that.file, fileIsString = typeof file === 'string', fileIsBase64 = /^data:/.test(file), img = new Image(),//创建img对象 canvas = document.createElement('canvas'),//创建canvas,用于后续的图片加载,旋转压缩 blob = fileIsString ? file : URL.createObjectURL(file);//创建bolb用于存放图片二进制数据 that.img = img; that.blob = blob; that.canvas = canvas; if (fileIsString) { that.fileName = fileIsBase64 ? 'base64.jpg' : (file.split('/').pop()); } else { that.fileName = file.name; } if (!document.createElement('canvas').getContext) { throw new Error('浏览器不支持canvas'); } return new Promise(function (resolve, reject) { img.onerror = function () { var err = new Error('加载图片文件失败'); reject(err); throw err; }; img.onload = function () { that._getBase64() .then(function (base64) { if (base64.length < 10) { var err = new Error('生成base64失败'); reject(err); throw err; } return base64; }) .then(function (base64) { var formData = null; // 压缩文件太大就采用源文件,且使用原生的FormData() @source #55 if (typeof that.file === 'object' && base64.length > that.file.size) { formData = new FormData(); file = that.file; } else { formData = new BlobFormDataShim.FormData(); file = dataURItoBlob(base64); } formData.append(that.defaults.fieldName, file, (that.fileName.replace(/\..+/g, '.jpg'))); resolve({ formData : formData, fileLen : +file.size, base64 : base64, base64Len: base64.length, origin : that.file, file : file }); // 释放内存 for (var p in that) { if (!that.hasOwnProperty(p)) continue; that[p] = null; } URL.revokeObjectURL(that.blob); }); }; // 如果传入的是base64在移动端会报错 !fileIsBase64 && (img.crossOrigin = "*"); img.src = blob; });};Lrz.prototype._getBase64 = function () { var that = this, img = that.img, file = that.file, canvas = that.canvas; return new Promise(function (resolve) { try { // 传入blob在android4.3以下有bug exif.getData(typeof file === 'object' ? file : img, function () { that.orientation = exif.getTag(this, "Orientation"); that.resize = that._getResize(); that.ctx = canvas.getContext('2d'); canvas.width = that.resize.width; canvas.height = that.resize.height; // 设置为白色背景,jpg是不支持透明的,所以会被默认为canvas默认的黑色背景。 that.ctx.fillStyle = '#fff'; that.ctx.fillRect(0, 0, canvas.width, canvas.height); // 根据设备对应处理方式 if (UA.oldIOS) { that._createBase64ForOldIOS().then(resolve); } else { that._createBase64().then(resolve); } }); } catch (err) { // 这样能解决低内存设备闪退的问题吗? throw new Error(err); } });};Lrz.prototype._createBase64ForOldIOS = function () { var that = this, img = that.img, canvas = that.canvas, defaults = that.defaults, orientation = that.orientation; return new Promise(function (resolve) { require(['megapix-image'], function (MegaPixImage) { var mpImg = new MegaPixImage(img); if ("5678".indexOf(orientation) > -1) { mpImg.render(canvas, { width : canvas.height, height : canvas.width, orientation: orientation }); } else { mpImg.render(canvas, { width : canvas.width, height : canvas.height, orientation: orientation }); } resolve(canvas.toDataURL('image/jpeg', defaults.quality)); }); });};Lrz.prototype._createBase64 = function () { var that = this, resize = that.resize, img = that.img, canvas = that.canvas, ctx = that.ctx, defaults = that.defaults, orientation = that.orientation; // 调整为正确方向 switch (orientation) { case 3: ctx.rotate(180 * Math.PI / 180); ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height); break; case 6: ctx.rotate(90 * Math.PI / 180); ctx.drawImage(img, 0, -resize.width, resize.height, resize.width); break; case 8: ctx.rotate(270 * Math.PI / 180); ctx.drawImage(img, -resize.height, 0, resize.height, resize.width); break; case 2: ctx.translate(resize.width, 0); ctx.scale(-1, 1); ctx.drawImage(img, 0, 0, resize.width, resize.height); break; case 4: ctx.translate(resize.width, 0); ctx.scale(-1, 1); ctx.rotate(180 * Math.PI / 180); ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height); break; case 5: ctx.translate(resize.width, 0); ctx.scale(-1, 1); ctx.rotate(90 * Math.PI / 180); ctx.drawImage(img, 0, -resize.width, resize.height, resize.width); break; case 7: ctx.translate(resize.width, 0); ctx.scale(-1, 1); ctx.rotate(270 * Math.PI / 180); ctx.drawImage(img, -resize.height, 0, resize.height, resize.width); break; default: ctx.drawImage(img, 0, 0, resize.width, resize.height); } return new Promise(function (resolve) { if (UA.oldAndroid || UA.mQQBrowser || !navigator.userAgent) { require(['jpeg_encoder_basic'], function (JPEGEncoder) { var encoder = new JPEGEncoder(), img = ctx.getImageData(0, 0, canvas.width, canvas.height); resolve(encoder.encode(img, defaults.quality * 100)); }) } else { resolve(canvas.toDataURL('image/jpeg', defaults.quality)); } });};Lrz.prototype._getResize = function () { var that = this, img = that.img, defaults = that.defaults, width = defaults.width, height = defaults.height, orientation = that.orientation; var ret = { width : img.width, height: img.height }; if ("5678".indexOf(orientation) > -1) { ret.width = img.height; ret.height = img.width; } // 如果原图小于设定,采用原图 if (ret.width < width || ret.height < height) { return ret; } var scale = ret.width / ret.height; if (width && height) { if (scale >= width / height) { if (ret.width > width) { ret.width = width; ret.height = Math.ceil(width / scale); } } else { if (ret.height > height) { ret.height = height; ret.width = Math.ceil(height * scale); } } } else if (width) { if (width < ret.width) { ret.width = width; ret.height = Math.ceil(width / scale); } } else if (height) { if (height < ret.height) { ret.width = Math.ceil(height * scale); ret.height = height; } } // 超过这个值base64无法生成,在IOS上 while (ret.width >= 3264 || ret.height >= 2448) { ret.width *= 0.8; ret.height *= 0.8; } return ret;};/** * 获取当前js文件所在路径,必须得在代码顶部执行此函数 * @returns {string} */function getJsDir (src) { var script = null; if (src) { script = [].filter.call(document.scripts, function (v) { return v.src.indexOf(src) !== -1; })[0]; } else { script = document.scripts[document.scripts.length - 1]; } if (!script) return null; return script.src.substr(0, script.src.lastIndexOf('/'));}/** * 转换成formdata * @param dataURI * @returns {*} * * @source http://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata */function dataURItoBlob (dataURI) { // convert base64/URLEncoded data component to raw binary data held in a string var byteString; if (dataURI.split(',')[0].indexOf('base64') >= 0) byteString = atob(dataURI.split(',')[1]); else byteString = unescape(dataURI.split(',')[1]); // separate out the mime component var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; // write the bytes of the string to a typed array var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new BlobFormDataShim.Blob([ia.buffer], {type: mimeString});}
2.图片的EXIF-Orientation信息
查询了一些相关的文章,简单的了解到EXIF-Orientation信息,原来在我们拍照的时候,拍照完成了,有一些相机是加入了EXIF-Orientation信息进去图片,这样一来,在我们查看相片的时候,它那个查看器会自动帮你旋转照片,而不用像以前那样,怎么照的就怎么显示,看的时候,还得自己去转动相机。
EXIF-Orientation信息的可参阅:Exif的Orientation信息说明
我从上面这篇博文摘取一段下来作为记录吧。
其中1836是我们最常用的几个旋转,第一行的红框,就是我们相机拍摄时的旋转状态,第二行,就是我们相机摆正后,图片实际存储的状态,到第三行,我们通过相机查看图片的时候,相机根据图片存入的EXIF-Orientation信息进行旋转,比如这里的8,在说明你拍照的时候,相机是逆时针旋转90度拍照,当你相机顺时针旋转90度来查看的时候,那么相机就会把图片逆时针选择90度,这样,在摆正相机的时候,就看到的是一个天在天,地在地的照片。
除了1836,还有另外的2754的EXIF-Orientation信息,分表就是通过1的镜面翻转后再进行角度旋转,看图理解起来有点困难,可以参照以下的表格辅助理解。
好了,此博客记录到此,如有错误或不解之处,望各位读者留下您宝贵的评论,thanks!
- JS对图片进行base64压缩以及图片的EXIF-Orientation信息
- 图片 EXIF 信息中旋转参数 Orientation 的理解
- 利用exif js及脚本修正图片的orientation显示
- 七牛---关于图片EXIF信息中旋转参数Orientation的理解
- C#压缩图片时保留原始的Exif信息
- Exif的Orientation信息说明
- 获取图片的exif信息
- jpeg图片的exif信息
- js压缩图片base64长度
- 对图片进行压缩
- 对图片进行压缩
- 对图片进行压缩
- 对图片进行压缩
- 修改图片exif信息
- Android 端读取图片exif信息,并对其进行修改
- 用C#读取图片的EXIF信息
- 获取图片的EXIF信息如此困难?
- flex获取图片的EXIF信息
- 变革OTT产业,苏宁PPTV打造家庭互联网第一开放平台
- Android keyboardView字体样式修改
- Java基础
- Linux实战教学笔记21-Rsync数据同步工具
- Android 水平平均布局
- JS对图片进行base64压缩以及图片的EXIF-Orientation信息
- spark学习笔记-spark基本概念与框架理解
- 高版本mysql向低版本mysql导入数据
- 网络编程
- 蓝桥杯练习: 算法训练 6-1 递归求二项式系数值
- 挑战练习题2.3动态规划 poj2385Apple Catching dp
- ActiveMQ介绍及Spring整合实例
- Kafka内部网络框架
- Leetcode 77