uniapp 使用uView框架 upload组件上传前压缩图片
根据Uview官方文档所说,若是小程序和app项目,上传使用压缩图片,可以配置u-upload组件的sizeType属性为'compress',但这对H5是无效的。在实际开发中,图片压缩上传经常是一个必要的需求,所以本篇文章主要讲的是在H5项目中如何使用Uview的upload组件进行压缩图片。
·
根据Uview官方文档所说,若是小程序和app项目,上传使用压缩图片,可以配置u-upload组件的sizeType属性为'compress',但这对H5是无效的。在实际开发中,图片压缩上传经常是一个必要的需求,所以本篇文章主要讲的是在H5项目中如何使用Uview的upload组件进行压缩图片。
首先在utils中封装压缩图片的方法:
tool.js
/**
* 图片压缩
* imgSrc 地址
* scale 压缩质量 0-1
* type 文件类型
*/
const compressImg = (imgSrc, scale, type, callback) => {
// uni.$u.toast('压缩中')
var img = new Image();
img.src = imgSrc;
img.onload = function() {
var that = this;
var h = (img.height * scale).toFixed(0); // 默认按质量比例压缩
var w = (img.width * scale).toFixed(0);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var width = document.createAttribute("width");
width.nodeValue = w;
var height = document.createAttribute("height");
height.nodeValue = h;
canvas.setAttributeNode(width);
canvas.setAttributeNode(height);
ctx.drawImage(that, 0, 0, w, h);
var base64 = canvas.toDataURL('image/jpeg', scale); //压缩比例
canvas = null;
if (type == 'base64') {
let data = {
size: getBase64Size(base64),
type: type,
source: base64
}
callback(base64);
} else {
let blob = base64ToBlob(base64);
console.log('压缩后的大小', blob.size);
const blobUrl = window.URL.createObjectURL(blob); //blob地址
blob.source = blobUrl
callback(blob);
}
}
};
/**base转Blob */
const base64ToBlob = (base64) => {
var arr = base64.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
});
};
/**获取base64的文件大小 */
const getBase64Size = () => {
let size = 0;
if (base64Str) { // 获取base64图片byte大小
const equalIndex = base64Str.indexOf('='); // 获取=号下标
if (equalIndex > 0) {
const str = base64Str.substring(0, equalIndex); // 去除=号
const strLength = str.length;
const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
size = Math.floor(fileLength); // 向下取整
} else {
const strLength = base64Str.length;
const fileLength = strLength - (strLength / 8) * 2;
size = Math.floor(fileLength); // 向下取整
}
} else {
size = null;
}
return size
};
在upload-image.vue页面调用
/** 压缩图片*/
compressImg(source, compressionRatio) {
let that = this;
return new Promise((resolve, reject) => {
that.$.compressImg(source.url, compressionRatio, source.type, compressRes => {
resolve(compressRes);
})
}).then((res) => {
source.size = res.size
// window.URL.revokeObjectURL(source.url) // 删除被压缩的缓存文件,这里注意,如果是相册选择上传,可能会删除相册的图片
source.url = res.source
source.thumb = res.source
return source
}).catch(err => {
console.log('图片压缩失败', err)
})
},
完整的upload-image.vue组件代码:
<!-- 上传文件组件 -->
<template>
<view @imgList="imgList" style="margin-left: 20rpx;">
<u-upload :fileList="fileList1.length === 0 ? files : fileList1" :sizeType="sizeType" @afterRead="afterRead"
@delete="deletePic" name="1" :maxCount="8" multiple :max-size="20 * 1024 * 1024" :previewFullImage="true"
:limitType="limitType" @oversize="oversize" @clickPreview="clickPreview" width="70" height="70"
:disabled="isdisabled" :deletable="deletable">
<image v-show="!isdisabled" src="@/static/images/med-icon/u24719.png"></image>
</u-upload>
</view>
</template>
<script>
import {
localUrl,
localResourseUrl
} from '@/environments/environments.js';
import {
forEach,
indexOf,
result
} from 'lodash';
export default {
name: "upload-image",
props: {
/** 图片默认列表*/
files: {
type: Array
},
/** 是否禁用*/
isdisabled: {
type: Boolean,
default: false
},
/**是否显示删除图片的按钮 */
deletable: {
type: Boolean,
default: true
}
},
data() {
return {
fileList1: [], //图片列表
sizeType: ['compressed'],
limitType: ['png', 'jpg', 'jpeg'], //允许的图片后缀
fileMaxSize: 1 * 1024 * 1024, // 超出1M开启压缩
maxSize:20 * 1024 * 1024, //图片最大不能超过20M
fileMinSize: 5 * 1024 // 最小为5KB
};
},
methods: {
//文件大小超出限制
oversize() {
uni.showToast({
title: "图片最大不能超过20M",
icon: 'none'
})
},
// 删除图片
deletePic(event) {
if (this.fileList1.length === 0) {
this.files.splice(event.index, 1)
} else {
this[`fileList${event.name}`].splice(event.index, 1)
}
this.imgList(this.fileList1)
},
//点击图片触发
clickPreview(url, lists, name) {
console.log('点击预览:', url, lists, name);
},
/**获取文件大小倍数,生成质量比*/
getCompressionRatio(fileSize) {
const multiple = (fileSize / this.fileMaxSize).toFixed(2);
let compressionRatio = 1;
if (multiple > 5) {
compressionRatio = 0.5
} else if (multiple > 4) {
compressionRatio = 0.6
} else if (multiple > 3) {
compressionRatio = 0.7
} else if (multiple > 2) {
compressionRatio = 0.8
} else if (multiple > 1) {
compressionRatio = 0.9
} else {
compressionRatio = 2
}
return compressionRatio;
},
// 读取图片
async afterRead(event) {
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
let lists = [].concat(event.file);
let fileListLen = this[`fileList${event.name}`].length;
for (let index in lists) {
const item = lists[index];
const fileSize = item.size;
const fileName = item.name ?? '';
if(!this.isAssetTypeAnImage(item.name)) {
this.$.msg('不允许该文件类型上传');
return false
}
console.log('文件大小',fileSize);
if (fileSize > this.fileMaxSize) {
const compressionRatio = this.getCompressionRatio(fileSize);
if (compressionRatio > 1) {
this.$.msg('文件' + fileName + '大于10M');
return false
}
// 超出1M自动压缩图片
await this.compressImg(item, compressionRatio)
if (item.size > this.maxSize) {
this.$.msg('文件' + fileName + '压缩后超出20M')
return false
}
}
if (item.size < this.fileMinSize) {
this.$.msg('文件' + fileName + '不能小于5KB');
return false
}
this[`fileList${event.name}`].push({
...item,
status: 'uploading',
message: '上传中'
})
}
for (let i = 0; i < lists.length; i++) {
const result = await this.uploadFilePromise(lists[i].url); //调用上传图片的方法
if (!result.Success) {
uni.$showMsg(result.Message);
const index = this.fileList1.findIndex(event => event.name === lists[i].name)
if (index !== -1) return this.fileList1.splice(index, 1);
}
// 垃圾回收
// window.URL.revokeObjectURL(lists[i].url);
let item = this[`fileList${event.name}`][fileListLen]
this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
status: 'success',
message: '',
url: lists[i].url,
res: result
}))
fileListLen++
}
this.imgList(this.fileList1);
},
/** 压缩图片*/
compressImg(source, compressionRatio) {
let that = this;
return new Promise((resolve, reject) => {
that.$.compressImg(source.url, compressionRatio, source.type, compressRes => {
resolve(compressRes);
})
}).then((res) => {
source.size = res.size
// window.URL.revokeObjectURL(source.url) // 删除被压缩的缓存文件,这里注意,如果是相册选择上传,可能会删除相册的图片
source.url = res.source
source.thumb = res.source
return source
}).catch(err => {
console.log('图片压缩失败', err)
})
},
//父组件清空图片列表
clearImgList() {
this.fileList1 = []
},
//返回选择的图片列表
imgList(list) {
// console.log(list);
let arr = []
let newArr = []
list.map(item => {
arr.push(item.res)
})
arr.map(x => {
newArr.push(x.Data[0])
})
this.$emit('imgList', Array.from(new Set(newArr)))
},
/**判断文件类型是否正确 */
isAssetTypeAnImage(ext) {
const str = ext.split('.')[1];
return this.limitType.indexOf(str.toLowerCase()) !== -1;
},
/**调用上传图片的接口 */
uploadFilePromise(urls) {
return new Promise((resolve, reject) => {
let a = uni.uploadFile({
url: `${localUrl}system/file/Uploads`,
name: 'files',
filePath: urls,
success: (res) => {
setTimeout(() => {
resolve(JSON.parse(res.data))
}, 500)
},
fail:(err) => {
reject(err);
}
});
})
},
}
}
</script>
<style lang="scss">
image {
width: 120rpx;
height: 116rpx;
}
</style>
注意:在图片压缩后,通过uni.uploadFile()上传的文件的FileName可能没有带后缀名,而后端大多数是根据FileName的后缀名来判断可上传的文件类型的,这时候就会出现接口对文件类型判断错误导致文件上传失败的情况。对于这个问题,我也是在网上查了很久也没有找到一个完美的解决方案。最后我换了个思路,在后端接口改为使用ContentType来校验文件类型,终于解决了这个问题!
更多推荐
已为社区贡献1条内容
所有评论(0)