【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;
}
}