移动端原生JS上传图片(含图片压缩和方向处理、ajax上传)

基本思路

  • 监听 input file 的 change 事件,获取到上传的文件信息
  • 使用 FileReader 对象读取文件的 base64 格式,赋值给 img 显示
  • 将图片绘制在 canvas 上,控制图片绘制的宽度,超过宽度,等比缩放。
  • 利用 exif.js 获取图片的方向信息,解决 ios 上竖直照片翻转

基础代码

你只需要一个简简单单的 input 即可,当然,要显示预览图片,肯定还需要一个 img 标签。此外,原生的 input 标签比较丑,为了美观起见,一般都会把 input 透明度设置成 0,然后自定义个按钮的样式盖在上面,样式这里就不详细讲了,easy。

<input type="file" name="image" accept=“image/*” onchange='handleInputChange' id="upload-img">
<img src="" class="prev-img">

兼容性

  • 安卓上不支持 multiple 属性,即不能一次多选
  • 因为调用的是系统原生的文件读取,每个机型显示的页面不一样,难以测试

完整的封装 Class 如下,如果不需要压缩或者方向处理,直接注释即可。

export default class Uploader {
  constructor() {

  }

  /**
   * 把base64格式转化成Blob格式
   * @param  {[type]} urlData [description]
   * @return {[type]}         [description]
   */
  convertBase64UrlToBlob(urlData) {
    var arr = urlData.split(','),
      mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {
      type: mime
    });
  }

  /**
   * 外部调用的方法 完成图片的压缩
   * @param  {[type]}   file     需要处理的file对象
   * @param  {[type]}   obj      传入的配置 quality 压缩的质量
   * @param  {Function} callback 完成压缩之后的回调函数
   * @return {[type]}            [description]
   */
  photoCompress(file, obj, callback) {
    // 使用HTML5的FileReader开始读取File对象中的内容
    let setting = obj;
    var ready = new FileReader();
    ready.readAsDataURL(file);
    var that = this;
    // 判断图片需不需要压缩
    if (file.size / 1024 < 500) {
      setting.quality = .9;
    } else {
      setting.quality = .5;
    }
    
    // 获取图片源信息 判断照片方向
    ready.onload = function () {
      var _this2 = this;
      that.getOrientation(file).then(function(o){
      // setting.orientation = EXIF.getTag(file, "Orientation");
      setting.orientation = o;
      var re = _this2.result;
      that.canvasDataURL(re, setting, callback, file)
      })
    }
    


  }

  /**
   * canvas绘制
   * @param  {[type]}   result     [description]
   * @param  {[type]}   obj      [description]
   * @param  {Function} callback [description]
   * @return {[type]}            [description]
   */
  canvasDataURL(result, obj, callback, file) {
    var img = new Image();
    img.src = result;
    var _this = this;
    img.onload = function() {
      var that = this;
      var w, h, scale, quality;
      scale = that.width / that.height;
      if (that.width > 1200) {
        w = 1200;
        h = w / scale;
      } else {
        w = that.width;
        h = that.height;
      }
      //生成canvas
      var canvas = document.createElement('canvas');
      var ctx = canvas.getContext('2d');
      // 创建属性节点
      var anw = document.createAttribute("width");
      anw.nodeValue = w;
      var anh = document.createAttribute("height");
      anh.nodeValue = h;
      canvas.setAttributeNode(anw);
      canvas.setAttributeNode(anh);
      // 调整图片方向
      // console.log(obj.orientation)
      if (obj.orientation && obj.orientation != "" && obj.orientation != 1) {
        switch (obj.orientation) {
          case 6: //需要顺时针(向左)90度旋转
            _this.rotateImg(that, w, h, 'left', canvas, ctx);
            break;
          case 8: //需要逆时针(向右)90度旋转
            _this.rotateImg(that, w, h, 'right', canvas, ctx);
            break;
          case 3: //需要180度旋转
            _this.rotateImg(that, w, h, 'right', canvas, ctx); //转两次
            _this.rotateImg(that, w, h, 'right', canvas, ctx);
            break;
        }
      } else {
        ctx.drawImage(that, 0, 0, w, h);
      }
      // 压缩图像质量
      var base64 = canvas.toDataURL('image/jpeg', obj.quality);
      // 回调函数返回base64的值
      callback(base64);
    }

  }

  getObjectURL(file) {
    var url = null;
    if (window.createObjectURL != undefined) {
      url = window.createObjectURL(file);
    } else if (window.URL != undefined) { // mozilla(firefox)
      url = window.URL.createObjectURL(file);
    } else if (window.webkitURL != undefined) { // webkit or chrome
      url = window.webkitURL.createObjectURL(file);
    }
    return url;
  }


  rotateImg(img, w, h, direction, canvas, ctx) {
    //最小与最大旋转方向,图片旋转4次后回到原方向
    var min_step = 0;
    var max_step = 3;
    //var img = document.getElementById(pid);
    if (img == null) return;
    //img的高度和宽度不能在img元素隐藏后获取,否则会出错
    var height = h;
    var width = w;
    //var step = img.getAttribute('step');
    var step = 2;
    if (step == null) {
      step = min_step;
    }
    if (direction == 'right') {
      step++;
      //旋转到原位置,即超过最大值
      step > max_step && (step = min_step);
    } else {
      step--;
      step < min_step && (step = max_step);
    }
    //旋转角度以弧度值为参数
    var degree = step * 90 * Math.PI / 180;
    // var ctx = canvas.getContext('2d');
    // alert(h)
    switch (step) {
      case 0:
        canvas.width = width;
        canvas.height = height;
        ctx.drawImage(img, 0, 0);
        break;
      case 1:
        canvas.width = height;
        canvas.height = width;
        ctx.rotate(degree);
        ctx.drawImage(img, 0, -height,w,h);
        break;
      case 2:
        canvas.width = width;
        canvas.height = height;
        ctx.rotate(degree);
        ctx.drawImage(img, -width, -height,w,h);
        break;
      case 3:
        canvas.width = height;
        canvas.height = width;
        ctx.rotate(degree);
        ctx.drawImage(img, -width, 0,w,h);
        break;
    }
  }

  ajaxThen(data) {
    return new Promise((resolve, reject) => {
      var form = new FormData();
      form.append('image', data);
      // console.log(data,form)
      $.ajax({
        url: '/home/image',
        data: form,
        method: 'post',
        dataType: 'json',
        contentType: false,
        processData: false,
        mimeType: "multipart/form-data",
        success: function(res) {
          resolve(res)
        }
      })
    })

  }

  getOrientation(file) {
    return new Promise((resolve, reject) => {
      EXIF.getData(file, () => {
        let o = EXIF.getTag(file, "Orientation");
        resolve(o);
        reject(o);
      })
    })
  }
}
posted @ 2018-10-10 15:21  下小朋友  阅读(2854)  评论(0编辑  收藏  举报