今天是2018年5月的最后一天了,纪念一下炎热的五月。
解决思路:单张上传做个示范
1.<label>嵌套<input type=file /> 选中图片,trigger input 的onchange 事件,通过event 取得其files ;
2.通过var fReader = new FileReader(),然后用fReader .readAsDataURL(file)将文件转为base64格式,接下来fReader对象会自动触发fReader .onload = function(e){ 此处e.target.result 就是base64格式的有效图片url,img标签可以直接使用,当img.src=e.target.result后,img.onload=function(){}又会自动触发}
3.img.onload函数触发时,我们必须在这个过程中设置照片的最大显示尺寸,用一个额外引入的exif.js判断照片的orientation方向(详解见后面),再用canvas.getContext("2d").rotate(弧度)处理orientation,接着用canvas.getContext("2d").drawImage(thisimg,x,y,imgW,imgH)来压缩图片。
4.要将照片上传到后台,则用canvas.toBlob方法将上面drawImage()的图片转成blob二进制数据 e.g.:canvas.toBlob( function(blobdata){
blobdata就是image转换为blob后的数据,我们用一个var formdata= new FormData(); formdata.append("filedname",blobdata);提交给后台。此时注意设置:processData:false//不要对内容转换为string;contentType:false;//不对内容进行编码类型设置
})
5.如果要预览,则img.src = canvas.toDataURL("image/jepg",1)就可以实现预览了。
主要js实现如下:
,filechangeOfFaceInput : function($e){ // 压缩图片需要的一些元素和对象 var files = event.target.files || event.dataTransfer.files || [];//可能多张[] //var file = this.files[0], var file = files[0], reader = new FileReader(), img = new Image(); var Orientation = null; if (file.type.indexOf("image") == 0 && file) {// vm.isSelectedAPicture = true; EXIF.getData(file, function() { Orientation = EXIF.getTag(this, 'Orientation'); }); reader.readAsDataURL(file);//读作url后,把file对象存在了reader中,file转为base64url reader.onload = function(e){ img.src = e.target.result;//转化为base64的url字符表示 ,img就可以用在src中 // ∵上面设置了base64形式的src所以,img重新onload。--PS:期间可以优化读取img的宽高(使用setInterval) img.onload = function () { var imgWidth = this.width, imgHeight = this.height; var maxw = 750,maxh = 1334; if (imgWidth > imgHeight && imgWidth > maxw) { imgWidth = maxw; imgHeight = Math.ceil(maxw / this.width * imgHeight); } else if (imgWidth < imgHeight && imgHeight > maxh) { imgWidth = Math.ceil(maxh /this.height * this.width); imgHeight = maxh; } var canvas = document.createElement("canvas"), ctx = canvas.getContext('2d'); 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: 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); } canvas.toBlob(function (blob) { vm.faceimgblob = blob; },file.type || 'image/png',1) vm.faceRecognitionViewUrl = canvas.toDataURL("image/jepg",1); }; }; }else{ vm.isSelectedAPicture = true; }; }, submitFaceIamge:function(){ var mydata = new FormData(); mydata.append("headImage", vm.faceimgblob); if (og.isNotEmpty(vm.faceimgblob)) { layer.load(); $.ajax({ type: "post", dataType: "json", data: mydata, cache: false, processData: false, contentType: false, url: "/wx/user/uploadFace", success: function (data) { if (data.status) { layer.msg('恭喜您上传成功。', {time: og.ogLayerTime, icon: 6}); //location.href = "/newPage/html/weixin/weixin-v1/myinfo.html"; vm.faceRecognitionViewUrlInit = data.data; //上传成功后关闭窗口 vm.isSelectedAPicture = false; var $faceoutdiv = $("#faceoutdiv"); $faceoutdiv.removeClass("og-displayblock"); } else { layer.alert(data.message); return false; } }, complete: function () { layer.closeAll("loading"); } }) } else { layer.msg('请您先选择一张图片。', {time: og.ogLayerTime, icon: 5}); return false; } }
以下是处理orientation方向的exif.js:
var EXIF = {}; var TiffTags = EXIF.TiffTags = { 0x0112: "Orientation" }; function imageHasData(img) { return !!(img.exifdata); } function getImageData(img, callback) { function handleBinaryFile(binFile) { var data = findEXIFinJPEG(binFile); img.exifdata = data || {}; if (callback) { callback.call(img); } } if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) { var fileReader = new FileReader(); fileReader.onload = function (e) { handleBinaryFile(e.target.result); }; fileReader.readAsArrayBuffer(img); } } function findEXIFinJPEG(file) { var dataView = new DataView(file); if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { return false; // not a valid jpeg } var offset = 2, length = file.byteLength, marker; while (offset < length) { if (dataView.getUint8(offset) != 0xFF) { return false; // not a valid marker, something is wrong } marker = dataView.getUint8(offset + 1); if (marker == 225) { return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2); } else { offset += 2 + dataView.getUint16(offset + 2); } } } function readTags(file, tiffStart, dirStart, strings, bigEnd) { var entries = file.getUint16(dirStart, !bigEnd), tags = {}, entryOffset, tag, i; for (i = 0; i < entries; i++) { entryOffset = dirStart + i * 12 + 2; tag = strings[file.getUint16(entryOffset, !bigEnd)]; tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd); } return tags; } function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) { var type = file.getUint16(entryOffset + 2, !bigEnd), numValues = file.getUint32(entryOffset + 4, !bigEnd), valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart, offset, vals, val, n, numerator, denominator; switch (type) { case 1: // byte, 8-bit unsigned int case 7: // undefined, 8-bit byte, value depending on field if (numValues == 1) { return file.getUint8(entryOffset + 8, !bigEnd); } else { offset = numValues > 4 ? valueOffset : (entryOffset + 8); vals = []; for (n = 0; n < numValues; n++) { vals[n] = file.getUint8(offset + n); } return vals; } case 2: // ascii, 8-bit byte offset = numValues > 4 ? valueOffset : (entryOffset + 8); return getStringFromDB(file, offset, numValues - 1); case 3: // short, 16 bit int if (numValues == 1) { return file.getUint16(entryOffset + 8, !bigEnd); } else { offset = numValues > 2 ? valueOffset : (entryOffset + 8); vals = []; for (n = 0; n < numValues; n++) { vals[n] = file.getUint16(offset + 2 * n, !bigEnd); } return vals; } case 4: // long, 32 bit int if (numValues == 1) { return file.getUint32(entryOffset + 8, !bigEnd); } else { vals = []; for (n = 0; n < numValues; n++) { vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd); } return vals; } case 5: // rational = two long values, first is numerator, second is denominator if (numValues == 1) { numerator = file.getUint32(valueOffset, !bigEnd); denominator = file.getUint32(valueOffset + 4, !bigEnd); val = new Number(numerator / denominator); val.numerator = numerator; val.denominator = denominator; return val; } else { vals = []; for (n = 0; n < numValues; n++) { numerator = file.getUint32(valueOffset + 8 * n, !bigEnd); denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd); vals[n] = new Number(numerator / denominator); vals[n].numerator = numerator; vals[n].denominator = denominator; } return vals; } case 9: // slong, 32 bit signed int if (numValues == 1) { return file.getInt32(entryOffset + 8, !bigEnd); } else { vals = []; for (n = 0; n < numValues; n++) { vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd); } return vals; } case 10: // signed rational, two slongs, first is numerator, second is denominator if (numValues == 1) { return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd); } else { vals = []; for (n = 0; n < numValues; n++) { vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * n, !bigEnd); } return vals; } } } function getStringFromDB(buffer, start, length) { var outstr = ""; for (n = start; n < start + length; n++) { outstr += String.fromCharCode(buffer.getUint8(n)); } return outstr; } function readEXIFData(file, start) { if (getStringFromDB(file, start, 4) != "Exif") { return false; } var bigEnd, tags, tag, exifData, gpsData, tiffOffset = start + 6; // test for TIFF validity and endianness if (file.getUint16(tiffOffset) == 0x4949) { bigEnd = false; } else if (file.getUint16(tiffOffset) == 0x4D4D) { bigEnd = true; } else { return false; } if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) { return false; } var firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd); if (firstIFDOffset < 0x00000008) { return false; } tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd); return tags; } EXIF.getData = function (img, callback) { if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false; if (!imageHasData(img)) { getImageData(img, callback); } else { if (callback) { callback.call(img); } } return true; } EXIF.getTag = function (img, tag) { if (!imageHasData(img)) return; return img.exifdata[tag]; }
orientation问题参考:
https://blog.csdn.net/linlzk/article/details/48652635
主要内容:
html5+canvas进行移动端手机照片上传时,发现ios手机上传竖拍照片会逆时针旋转90度,横拍照片无此问题;Android手机没这个问题。
因此解决这个问题的思路是:获取到照片拍摄的方向角,对非横拍的ios照片进行角度旋转修正。
利用exif.js读取照片的拍摄信息,详见 http://code.ciaoca.com/javascript/exif-js/
这里主要用到Orientation属性。
Orientation
属性说明如下:
旋转角度 | 参数 |
0° | 1 |
顺时针90° | 6 |
逆时针90° | 8 |
180° | 3 |
brief summary :
以上代码是结合vue一起写的,项目已上线,大家放心使用,有更好的方法,欢迎大家提出来,good luck^_^。