uniapp 使用uView框架 upload组件上传前压缩图片

文章介绍了在H5项目中,针对uni-app框架,如何利用Uview的upload组件配合JavaScript编写图片压缩方法,以满足图片压缩上传的需求。通过封装compressImg函数进行图片压缩,并在upload组件的afterRead事件中调用此方法,确保图片大小符合要求后再上传。
摘要由CSDN通过智能技术生成

根据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来校验文件类型,终于解决了这个问题!

        

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值