uniapp 虽然在某种情况下是个很好用的框架, 但还是避免不了踩坑, 而坑在社区或官网或百度里都没法找到能立即适用的解决方案. 今天遇到的一个大问题就是使用 uni.chooseImage 上传图片或者拍照在部分手机上图片出现旋转, 同时就算设置了压缩结局还是没有压缩图片的问题.
今天我给大家一份完整的解决方案, 就目前运行的项目来说, oppo, 小米, 苹果的手机出现旋转角度有顺时针, 逆时针 90 度以及旋转 180 度等问题, 目前测试均无问题.
因时间问题, 有些地方代码不一定是最简洁的, 大家如果有更好的方式欢迎大家留言, 共同学习进步.
第一步, 我们必须要知道图片旋转的方向 (exifjs 库是可以拿到图片的很多信息包括方向, 但在 uniapp 中无法使用, 所以这里没有用, 但大家可以尝试),uni.getImageInfo 号称是可以拿到该值, 但经过实验没有拿到, 所以我换了种方式, 该方式其实在网上也可搜索到, 我们直接看源码:
- export const common = {
- // 这里的获取 exif 要将图片转 ArrayBuffer 对象, 这里假设获取了图片的 baes64
- // 步骤一
- // base64 转 ArrayBuffer 对象
- base64ToArrayBuffer: (base64) => {
- base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
- var binary = atob(base64);
- var len = binary.length;
- var buffer = new ArrayBuffer(len);
- var view = new Uint8Array(buffer);
- for (var i = 0; i <len; i++) {
- view[i] = binary.charCodeAt(i);
- }
- return buffer;
- },
- // 步骤二, Unicode 码转字符串
- // ArrayBuffer 对象 Unicode 码转字符串
- getStringFromCharCode: (dataView, start, length) => {
- var str = '';
- var i;
- for (i = start, length += start; i <length; i++) {
- str += String.fromCharCode(dataView.getUint8(i));
- }
- return str;
- },
- // 步骤三, 获取 jpg 图片的 exif 的角度 (在 iOS 体现最明显)
- getOrientation: (arrayBuffer) => {
- var dataView = new DataView(arrayBuffer);
- var length = dataView.byteLength;
- var orientation;
- var exifIDCode;
- var tiffOffset;
- var firstIFDOffset;
- var littleEndian;
- var endianness;
- var app1Start;
- var ifdStart;
- var offset;
- var i;
- // Only handle JPEG image (start by 0xFFD8)
- if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
- offset = 2;
- while (offset <length) {
- if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
- app1Start = offset;
- break;
- }
- offset++;
- }
- }
- if (app1Start) {
- exifIDCode = app1Start + 4;
- tiffOffset = app1Start + 10;
- if (common.getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
- endianness = dataView.getUint16(tiffOffset);
- littleEndian = endianness === 0x4949;
- if (littleEndian || endianness === 0x4D4D /* bigEndian */ ) {
- if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
- firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
- if (firstIFDOffset>= 0x00000008) {
- ifdStart = tiffOffset + firstIFDOffset;
- }
- }
- }
- }
- }
- if (ifdStart) {
- length = dataView.getUint16(ifdStart, littleEndian);
- var standalone = Windows.navigator.standalone,
- userAgent = Windows.navigator.userAgent.toLowerCase(),
- Safari = /Safari/.test(userAgent),
- iOS = /iPhone|ipod|iPad/.test(userAgent);
- for (i = 0; i <length; i++) {
- offset = ifdStart + i * 12 + 2;
- if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */ ) {
- // 8 is the offset of the current tag's value
- offset += 8;
- // Get the original orientation value
- orientation = dataView.getUint16(offset, littleEndian);
- // Override the orientation with its default value for Safari (#120)
- // if (IS_SAFARI_OR_UIwebVIEW) {
- // dataView.setUint16(offset, 1, littleEndian);
- // }
- if (iOS) {
- if (!standalone && Safari) {
- dataView.setUint16(offset, 1, littleEndian);
- } else if (standalone && !Safari) {
- //standalone
- } else if (!standalone && !Safari) {
- dataView.setUint16(offset, 1, littleEndian);
- };
- }
- break;
- }
- }
- }
- return orientation;
- }
- }
第二步, 获取图片的旋转方向:
- urlTobase64(url) {
- uni.request({
- url: url,
- method: 'GET',
- responseType: 'arraybuffer',
- success: (res)=> {
- let base64 = uni.arrayBufferToBase64(res.data); // 把 arraybuffer 转成 base64
- base64 = 'data:image/jpeg;base64,' + base64; // 不加上这串字符, 在页面无法显示
- const or=common.getOrientation(common.base64ToArrayBuffer(base64))
- this.or=or
- this.compressImg(base64,or)
- }
- });
- },
第三步, 对需要旋转的图片旋转, 压缩, 这里设置的 canvas 的 width=300,height=400. 里面值需要以此为参考换算:
- compressImg(img,or){
- const ctx = uni.createCanvasContext('myCanvas')
- console.log(or)
- if(or==6){// 逆时针旋转了 90
- ctx.translate(300,0)
- ctx.rotate(Math.PI/2)
- ctx.drawImage(img, 0, 0,400, 300)
- }else if(or==3){// 逆时针旋转了 180
- ctx.translate(300,400)
- ctx.rotate(Math.PI)
- ctx.drawImage(img, 0, 0,400, 300)
- }else if(or==8){// 顺时针旋转 90
- ctx.translate(0,400)
- ctx.rotate(-Math.PI/2)
- ctx.drawImage(img, 0, 0,400, 300)
- }else{
- ctx.drawImage(img, 0, 0,300, 400)
- }
- ctx.draw(false,
- ()=>{
- uni.canvasToTempFilePath({
- x: 0,
- y: 0,
- width: 300,
- height:400,
- destWidth:300,
- destHeight:400,
- fileType:'jpg',
- canvasId: 'myCanvas',
- success:(res)=> {
- this.img=res.tempFilePath
- },
- fail:(err)=>{
- this.img=img
- }
- })
- })
- },
第四步, 通过 uni.chooseImage 上传图片:
- // 选择图片
- ChooseImage() {
- uni.showLoading({
- title: '照片获取中',
- mask: true
- });
- uni.chooseImage({
- count: 1, // 默认 9
- sizeType: ['compressed'], // 可以指定是原图还是压缩图, 默认二者都有
- sourceType: ['album', 'camera'], // 从相册选择
- success: (res) => {
- this.urlTobase64(res.tempFilePaths[0])
- uni.hideLoading()
- },
- fail:(err)=>{
- uni.hideLoading()
- }
- });
- },
至此, 一套完整的方案就已经产生了. 希望能帮到需要的朋友.
来源: http://www.jianshu.com/p/05df047f54af