vue 解决image-conversion图片处理插件压缩后图片底色变黑问题
官方文档https://www.npmjs.com/package/image-conversion
将el-upload封装为一个组件,并将图片上传到对象存储
引用方式
import SingleUpload from "@/components/upload/singleUpload"
//isWatermark是否需要水印
<SingleUpload :isWatermark="false" v-model="form.paramValue" ></SingleUpload>
changeFile方法判断是否是透明图片,如果是透明图片将底部改为透明色
<template>
<div>
<el-upload
class="avatar-uploader"
:action="dataObj.host"
:data="dataObj"
:show-file-list="false"
:before-upload="beforeUpload"
:on-remove="handleRemove"
:on-success="handleUploadSuccess"
:on-preview="handlePreview"
>
<div v-if="imageUrl" @click.stop="changeImg" class="single-avatar">
<el-image style="width:120px; height:120px" :src="imageUrl" :preview-src-list="[imageUrl]" fit="cover" />
<div class="close" @click.stop="close">
<i class="el-icon-error"></i>
</div>
</div>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</template>
<script>
import {policy} from './policy'
import { getCompressionQuality, compressImage } from './compressionUtils';
export default {
name: 'singleUpload',
props: {
value: String,
isWatermark: {
type: Boolean,
default: true
}
},
computed: {
imageUrl: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
},
},
data() {
return {
uploadImg:'',
dataObj: {
policy: '',
signature: '',
key: '',
ossaccessKeyId: '',
dir: '',
host: '',
},
dialogVisible: false
};
},
methods: {
emitInput(val) {
this.$emit('input', val)
},
changeImg(e){
console.log(e);
},
close(){
this.imageUrl='';
},
handleRemove(file, fileList) {
this.emitInput('');
},
handlePreview(file) {
this.dialogVisible = true;
},
beforeUpload(file) {
console.log(file,'file');
let _self = this;
return new Promise(async(resolve, reject) => {
const rusult = await this.changeFile(this, file, this.loadData, false);
policy().then(response => {
console.log(response.data,'response.data')
_self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessid;
_self.dataObj.key =response.data.dir + "/"+ this.uuid()+"_${filename}";
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
console.log( rusult,'rusult')
resolve(rusult)
}).catch(err => {
reject(false)
})
})
},
processImage(that, file, uploadData, isUnload){
const imgType = file.type.toUpperCase();
const isLt30M = file.size / 1024 / 1024 < 30;
const isLt2M = file.size / 1024 / 1024 < 2;
const isLtSize = file.size / 1024 / 1024;
console.log('压缩前图片size', file.size / 1024 / 1024);
// uploadData.isCompressed = 0
if (
imgType !== "IMAGE/JPG" &&
imgType !== "IMAGE/JPEG" &&
imgType !== "IMAGE/PNG"
) {
that.$message.error("请上传正确格式的图片");
return false;
}
if (!isLt30M) {
that.$message.error("上传的图片不能超过30Mb");
return false;
}
//压缩质量
const quality = getCompressionQuality(isLtSize,imgType);
//大于2M走压缩逻辑
console.log('quality', quality);
return compressImage(file, quality)
.then(compressedFile => {
console.log(compressedFile,'compressedFile');
return compressedFile;
})
.catch(err => {
console.error('Image compression error:', err);
return file;
});
},
changeFile(that, file, uploadData, isUnload){
return new Promise((resolve)=>{
const $this = this
$this.readFile(file).then(res => {
console.log(res,'readFile');
// base64格式
if (res) {
// 创建图片
$this.createImage(res).then(res2 => {
console.log(res2,'createImage');
if (res2) {
// 如果是透明图片,压缩前将底色改为透明背景,防止上传时有黑底
let url = ''
if(file.type == 'image/png'){
url = $this.transparentImg(res2)
}else{
url = this.isWatermark ? $this.createWatermark(res2) : res;
}
console.log(url)
const files = this.baseToFile(url)
console.log('image File:', files)
const imgType = files.type.toUpperCase();
const isLt30M = files.size / 1024 / 1024 < 30;
const isLt2M = files.size / 1024 / 1024 < 2;
const isLtSize = files.size / 1024 / 1024;
// uploadData.isCompressed = 0
if (
imgType !== "IMAGE/JPG" &&
imgType !== "IMAGE/JPEG" &&
imgType !== "IMAGE/PNG"
) {
that.$message.error("请上传正确格式的图片");
return false;
}
// if (!isLt30M) {
// that.$message.error("上传的图片不能超过30Mb");
// return false;
// }
//压缩质量
const quality = getCompressionQuality(isLtSize,imgType);
//大于2M走压缩逻辑
console.log('quality', quality);
return compressImage(files, quality)
.then(compressedFile => {
console.log(compressedFile,'compressedFile');
//对象存储需将file转为Blob对象
const resultFile = new Blob([compressedFile], { type: compressedFile.type });
resolve(resultFile)
})
.catch(err => {
console.error('Image compression error:', err);
resolve(files)
});
}
})
}
})
})
},
// 读取图片文件
readFile (file) {
return new Promise((resolve) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
resolve(reader.result)
}
})
},
// 生成图片
createImage (dataUrl) {
return new Promise((resolve) => {
const img = new Image()
img.src = dataUrl
img.onload = () => {
resolve(img)
}
})
},
// 写水印
createWatermark (img) {
const str = '热数字化管理平台'
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
console.log(img.width, img.height);
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
ctx.font = img.width > 3000?'30px Microsoft YaHei':'18px Microsoft YaHei'
ctx.fillStyle = 'rgba(255,255,255,0.4)'
ctx.textAlign = 'left'
ctx.textBaseline = 'Middle'
ctx.rotate(-0.2)
for (let i = 0; i <= 50; i++) {
ctx.fillText(str, 0, i * 100)
for (let j = 0; j <= 50; j++) {
ctx.fillText("热数字化管理平台", j * 340, i *100);
}
}
return canvas.toDataURL()
},
// 透明背景
transparentImg(img) {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = img.width
canvas.height = img.height
console.log(img.width, img.height);
ctx.fillStyle = 'rgba(255,255,255,0)'
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0)
return canvas.toDataURL()
},
// 图片 base64 转换 file 格式
baseToFile (base64) {
if (base64.includes('data:image/')) {
// console.log('base64: ', base64)
const fileArray = base64.split(',')
// 过滤出文件类型
const fileType = fileArray[0].match(/:(.*?);/)[1]
// atob 是对经过 base-64 编码的字符串进行解码
const bstr = atob(fileArray[1])
let n = bstr.length
// Uint8Array 数组类型表示一个 8 位无符号整型数组
const u8arr = new Uint8Array(n)
while (n--) {
// 返回字符串n个字符的 Unicode 编码
u8arr[n] = bstr.charCodeAt(n)
}
const second = new Date()
return new File([u8arr], second.getTime(), { type: fileType })
}
},
uuid() {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 32; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23];
var uuid = s.join("");
return uuid;
},
handleUploadSuccess(res, file) {
console.log("上传成功...")
this.uploadImg=this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name);
const imageUrl=this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name);
this.emitInput(imageUrl);
}
}
}
</script>
<style lang="scss">
.avatar-uploader{
width: 80px;
height: 80px;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 80px!important;
height: 80px!important;
line-height: 80px!important;
text-align: center;
}
.single-avatar {
width: 80px;
height: 80px;
display: block;
position: relative;
}
.close{
position: absolute;
top: 0;
right: 0;
cursor: pointer;
font-size: 20px;
i{
color:black;
top: 0;
right: 0;
}
}
</style>
compressionUtils.js文件
import * as imageConversion from 'image-conversion';
//压缩质量
export function getCompressionQuality(isLtSize,imgType) {
return 500;
}
//压缩逻辑
export function compressImage(file, quality) {
return new Promise((resolve, reject) => {
imageConversion.compressAccurately(file, 300)
.then((res) => {
console.log(res,'compress');
resolve(res);
// console.log('压缩后体积', res.size / (1024 * 1024));
})
.catch((error) => {
reject(error);
});
});
}