拍照直接上传or选择很大的原照片,经过canvas 压缩,处理IOS orientation(照片旋转)问题,完美上传

今天是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属性说明如下:

旋转角度 参数
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,

所以只用写成:就可以了

 

 

 

你可能感兴趣的:(JS上传文件)