今天是2018年5月的最后一天了,纪念一下炎热的五月。
解决思路:单张上传做个示范
1.
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.οnlοad=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^_^。
重点:如果你需要既能调用摄像头也能选择文件的功能,那么在android 和IOS上是不同的:
只能在android实现,在ios中只能直接打开摄像头
在ios中既能打开摄像头也可以的开文件。
capture可选三个属性:对应accept
accept表示打开的系统文件目录
capture表示的是系统所捕获的默认设备,camera:照相机;camcorder:摄像机;microphone:录音;其中还有一个属性multiple,支持多选,当支持多选时,multiple优先级高于capture,
所以只用写成:就可以了