移动端部分安卓手机(三星,小米)竖拍上传图片预览的时候发生旋转问题
移动端web 页面通过 input type= file 拍照的时候,部分手机图片出现量旋转了90度的问题,出现问题的机型为小米和三星,手机竖着拍的时候出现这种旋转的问题,横拍出来的照片是正常的,结合网上的解决办法总结如下:
主要是用 Orientation 这个参数
这个参数并不是所有的图片都有,不过通过手机拍出来的图片都是带有这个参数的,
旋转角度 | 参数值 |
0 | 1 |
顺时针90 | 6 |
逆时针90 | 8 |
180 | 3 |
参数为1 的时候显示正常,有问题的机型是竖拍的时候参数为6,(在实测中发现iPhone 6s ios 9系统上发现也是竖排参数为6,但是该手机的预览结果是正常的,这个原因暂时没有深究还)
想要拿到 Orientation 这个参数,可以通过exif.js 库来获取,github地址为:https://github.com/exif-js/exif-js
由于项目是vue 所以就使用 yarn add exif-js --save 来安装
exif.js获取Orientation的代码为:
EXIF.getData(file, function() { var Orientation = EXIF.getTag(this, 'Orientation'); });
该项目要求上传的图片能实现预览压缩,因此使用fileReader.readAsDataURL(file)来实现,
旋转用的是canvas 的rotate()方法实现的,(将上传的图片先用canvas绘制下来,绘制的过程中将需要旋转的转过来,然后在转化成base64来预览,以及传参给后端)
ctx.rotate(angle),
rotate方法的参数为旋转弧度,需要将角度转化为弧度:degress * Math.PI / 180,
压缩:手机拍出来的照片太大,而且使用 base64 编码的照片会比原照片大,那么上传的时候进行压缩就非常有必要的。现在的手机像素这么高,拍出来的照片宽高都有几千像素,用 canvas 来渲染这照片的速度会相对比较慢。
因此第一步需要先对上传照片的宽高做限制,判断宽度或高度是否超出哪个范围,则等比压缩其宽高。
var ratio = width / height;
if(imgWidth > imgHeight && imgWidth > xx){
imgWidth = xx;
imgHeight = Math.ceil(xx / ratio);
}else if(imgWidth < imgHeight && imgHeight > yy){
imgWidth = Math.ceil(yy * ratio);
imgHeight = yy;
}
解下来就是通过 canvas.toDataURL() 方法来压缩照片质量
canvas.toDataURL("image/jpeg", 1);
toDataURL() 方法返回一个包含图片展示的 data URI 。使用两个参数,第一个参数为图片格式,默认为 image/png。第二个参数为压缩质量,在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。
附上VUE项目中的完整代码为:
<template> <div class="photo"> <h1>发现身边的投资机会</h1> <div class="photo-input"> <input class="photo-file" type="file" name="upload" accept="image/*" mutiple="mutiple" @change="fileImg"> <div class="photo-button"> <img class="photo-logo" src="../../assets/photo-w.png" alt=""> <span>拍照识财</span> </div> </div> <div v-if="text">图片上传中...</div> <!--<img :src="imgBase64" alt="" id="preview">--> </div> </template> <script> import axios from 'axios' import { EXIF } from 'exif-js' export default { name: 'photo', mounted(){ }, data () { return { imgBase64: '', text:false } }, created () { localStorage.clear() window.aa = '' window.localStorage.clear() // localStorage.clear() }, methods: { fileImg (e) { let that = this // localStorage.setItem('imgbase', reader.result) console.log(e.target.files[0]) let Orientation let file = e.target.files[0] // this.imgPreview(e.target.files[0]) if(file){ EXIF.getData(file, function(){ // var a = EXIF.pretty(this) // alert(a[14]) Orientation = EXIF.getTag(this, "Orientation") // alert(Orientation) }); } let reader = new FileReader() let img = new Image() reader.readAsDataURL(e.target.files[0]) let canvas = document.createElement('canvas') let ctx = canvas.getContext('2d') reader.onload = (ev) => { // alert("图片转base64:"+reader.result) // this.imgBase64 = reader.result img.src = ev.target.result img.onload = function () { var imgWidth = this.width var imgHeight = this.height console.log(this.width) console.log(this.height) // alert(this.width) // alert(this.height) let ratio = imgWidth / imgHeight if (imgWidth > imgHeight && imgWidth > 600) { imgWidth = 400 imgHeight = Math.ceil(400 / ratio) } else if (imgWidth < imgHeight && imgHeight > 900) { imgWidth = Math.ceil(200 * ratio) imgHeight = 200 } canvas.width = imgWidth canvas.height = imgHeight if (Orientation && Orientation != 1) { switch (Orientation) { case 6: canvas.width = imgHeight canvas.height = imgWidth ctx.rotate(Math.PI / 2) ctx.drawImage(this, 0, -imgHeight, imgWidth, imgHeight); break; case 3: ctx.rotate(Math.PI); ctx.drawImage(this, -imgWidth, -imgHeight, imgWidth, imgHeight); break; case 8: // 旋转-90度 canvas.width = imgHeight; canvas.height = imgWidth; ctx.rotate(3 * Math.PI / 2); ctx.drawImage(this, -imgWidth, 0, imgWidth, imgHeight); break; } } else { ctx.drawImage(this, 0, 0, imgWidth, imgHeight); } this.imgBase64 = canvas.toDataURL("image/jpeg", 0.9) // console.log(this.imgBase64) window.aa = this.imgBase64 // console.log(window.aa) var len = this.imgBase64.length var size = len - (len / 8) * 2 console.log(size) // alert(size) that.$router.push('/result/industry') // reader.onload = (ev) => { // // alert("图片转base64:"+reader.result) // this.imgBase64 = reader.result // try { // // localStorage.setItem('imgBase64', reader.result) // window.aa = reader.result // } catch (error) { // // console.log("存储localstorage失败"+error) // // } // try { // // localStorage.setItem('imgBase64', reader.result) // // window.aa = reader.result // } catch (error) { // console.log("存储localstorage失败"+error) // } // alert('base64全局存储完成') // this.$router.push('/result/industry') // alert('push完成') } } }, imgPreview(file){ // let _this = this let Orientation console.log(file) //去获取拍照时的信息,解决拍出来的照片旋转问题 EXIF.getData(file, function(){ var a = EXIF.pretty(this) // alert(a[14]) // alert(a[Orientation]) console.log(a) Orientation = EXIF.getTag(this, "Orientation") // alert(EXIF.getTag(this, "Orientation")) alert(Orientation) }); console.log(Orientation) } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .photo{ background: #fff; padding:20px; } .photo h1{ display: block; font-size:20px; padding:10px 0; } .photo-input{ display: block; position: relative; height:80px; margin:50px 0; } .photo-file{ display:block; width:100%; height:50px; position: absolute; z-index:2; opacity: 0; } .photo-button{ display: flex; position: absolute; top: 0; background: #4CAF50; width: 80%; border-radius: 50px; height: 50px; left: 10%; align-items: center; justify-content: center; } .photo-button img{ display: block; width:34px; } .photo-button span{ display: block; font-size:16px; color:#fff; padding:0 8px; } </style>
参考链接为:https://www.imweb.io/topic/59559c01ad7fa941029740aa