欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Vue】FormData上传整合cropperjs图片裁剪

程序员文章站 2022-04-09 13:14:22
...

前言

我感觉上传这部分是一个比较碎片的知识点了,所以当时在做上传的模块的时候尽量做了一些整合,鉴于大部分上传都需要实现上传,展示等功能,所以这部分基本会囊括到,如果是需要裁剪的话请先:

npm i cropperjs -S

secdra的上传页面在这
http://www.secdra.com/upload

正文

首先贴上上传的dom

<div class="card upload-card" @dragover.prevent="_=>{}" @drop.prevent="upload($event,'drop')">
        <div class="image-box flex-box">
          <div class="upload-box" v-if="!drawUrl">
            <Btn type="file" color="primary" @change="upload($event,'button')">上传图片</Btn>
            <h3>拖拽一个图片到这里上传</h3>
            <p>支持jpg、png、jpeg格式</p>
          </div>
          <div class="view-box" v-if="drawUrl">
            <label class="flex-box">
              <img :src="drawUrl" :style="{height:proportion>=1?`100%`:`auto`,width:proportion<=1?`100%`:`auto`}">
              <input type="file" style="display: none" @change="upload($event,'button')">
            </label>
          </div>
        </div>
      </div>

接下来我们关注upload这个方法

 async upload($event, type) {
        let file;
        if (type === "button") {
          file = $event.target.files[0];
        } else if (type === "drop") {
          file = $event.dataTransfer.files[0]
        }
        if (!file) {
          return false;
        }
        if (!ioUtil.isImage(file)) {
          this.$notify({message: "请上传图片"});
          return false;
        }
        let url = URL.createObjectURL(file);
        let {width, height} = await ioUtil.getOffset(url);
        this.file = file;
        this.width = width;
        this.height = height;
        this.drawUrl = url;
},

我们拿到文件后直接构建FormData发送

  let form = new FormData();
  form.append("file", this.file);

不要忘记设置头信息

headers: {
  'Content-Type': 'multipart/form-data'
}

接下来使整合cropperjs进行裁剪
cropperjs文档请看
https://fengyuanchen.github.io/cropperjs/

首先我们初始化这个cropper,传入的参数主要是需要裁剪的img的dom,然后有一点要注意,这个dom的上层dom需要指定宽高,然后option就根据需求细看官方文档就可以了

this.headCropper = new Cropper(this.$refs["tailoringHeadImage"]._isVue ? this.$refs["tailoringHeadImage"].$el : this.$refs["tailoringHeadImage"], {
  aspectRatio: 1,
  viewMode: 1,
  background: false,
  zoomable: false
});

以上述的方法获得file后继续进行操作

this.tailoringHeadImage = URL.createObjectURL(file);
//写入需要裁剪图片
this.headCropper.replace(this.tailoringHeadImage);
//清除事件
$event.target.value = "";
//获取裁剪后比例图片canvas
this.headCropper.getCroppedCanvas()

然而这样是获取到的图片是根本不符合我们的要求的,因为我们在头像这方面裁剪后不仅要他比例一致,而且大小也要一致,所以我们通过一个工具方法来实现大小统一

let file = ioUtil.dataURLtoFile(ioUtil.getRoundedCanvas(this.headCropper.getCroppedCanvas(), 400, 400).toDataURL());

详细代码
https://github.com/JunJieFu/secdra-web/blob/master/components/pages/user/SelfHome.vue

以上用到了很多ioUtil方法,估计很多人会一头雾水,接下来就是这个工具方法的js

export default {
  dataURLtoFile(dataurl, filename = 'file') {
    let arr = dataurl.split(',');
    let mime = arr[0].match(/:(.*?);/)[1];
    let suffix = mime.split('/')[1];
    let bstr = atob(arr[1]);
    let n = bstr.length;
    let u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    return new File([u8arr], `${filename}.${suffix}`, {type: mime})
  },

  /**
   * @param canvas:HTMLCanvasElement
   * @returns {Promise<Blob>}
   */
  canvasToBlob(canvas) {
    return new Promise(function (resolve, reject) {
      try {
        canvas.toBlob(blobObj => resolve(blobObj));
      } catch (e) {
        reject(e)
      }
    })
  },

  /**
   *
   * @param url
   * @returns {Promise<*>}
   */
  getOffset(url) {
    let img = new Image();
    img.src = url;
    return new Promise(function (resolve, reject) {
      try {
        img.onload = _ => {
          resolve({width: img.width, height: img.height});
        }
      } catch (e) {
        reject(e)
      }
    })
  },

  getRoundedCanvas(sourceCanvas, width, height) {
    let canvas = document.createElement("canvas");
    let context = canvas.getContext("2d");
    // let width = sourceCanvas.width;
    let _width = width || sourceCanvas.width;
    let _height = height || sourceCanvas.height;
    canvas.width = _width;
    canvas.height = _height;
    context.imageSmoothingEnabled = true;
    context.drawImage(sourceCanvas, 0, 0, _width, _height);
    context.globalCompositeOperation = "destination-in";
    context.beginPath();
    context.fill();
    return canvas;
  },
  isImage(file, suffixList = ['jpg', 'png', 'jpeg']) {
    if (!file instanceof File) {
      throw new Error("not File")
    }
    if (file.type.indexOf("image/") === -1) {
      return false
    }
    if (!suffixList.isEmpty()) {
      let fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase();
      if (suffixList.indexOf(fileSuffix) === -1) {
        return false
      }
    }
    return true;
  }
}

相关标签: vue