草庐IT

【uniapp】uniapp 使用uView框架 upload组件压缩图片

Eric | 墜夢 2023-03-28 原文

tips:如果本文对你有用,请爱心点个赞,提高排名,让这篇文章帮助更多的人。谢谢大家!比心❤~
如果解决不了,可以在文末加我微信,进群交流。

技术选型

背景

uView框架upload文件上传组件中没有h5端控制压缩的参数,超出文件大小也没有响应的提示,但是一般的上传文件场景,是需要控制文件大小的。


效果图

原图是4.8M,长宽为:3024*4032



封装图片压缩工具方法

  • 封装util.js
// 图片压缩
/**
 * imgSrc 地址
 * scale 压缩质量 0-1
 * type 文件类型
 */
export function 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, blob.size, blob.type)
			const blobUrl = window.URL.createObjectURL(blob); //blob地址
			blob.source = blobUrl
			callback(blob);
		}
	}
}

// base转Blob
export function 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的文件大小
export function getBase64Size(base64Str) {
	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
}
index.vue中引入util.js,并在上传时使用
<template>
	<view class="components-wrap">
		<u-form-item class='form-item-box' labelWidth='auto' :prop="column.name" borderBottom :ref="column.name">
			<u-upload class="upload-wrap" width="102" :fileList="fileList" @afterRead="afterRead" @delete="deletePic"
				:sizeType="sizeType" @clickPreview="clickPreview" :name="column.name" multiple :disabled="disabled"
				:formkey="formkey">
				<view class="material-add">
					<u-button class='upload-btn' :disabled="disabled" text="上传图片"></u-button>
				</view>
			</u-upload>
		</u-form-item>
	</view>
</template>
<script>
	import {
		compressImg
	} from '@/utils/util.js'
	export default {
		data() {
			return {
				currentValue: false,
				fileList: [],
				sizeType: ['compressed'],
				fileMaxSize: 2 * 1024 * 1024, // 默认最大为2M
				fileMinSize: 50 * 1024 // 最小为50KB
			}
		},
		methods: {
			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) {
				// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
				let lists = [].concat(event.file)
				let fileListLen = this[`fileList`].length

				for (let index in lists) {
					const item = lists[index]
					const fileSize = item.size
					const fileName = item.name ?? ''
					if (fileSize > this.fileMaxSize) {
						const compressionRatio = this.getCompressionRatio(fileSize)
						if (compressionRatio > 1) {
							uni.$u.toast('文件' + fileName + '大于10M')
							return false
						}
						// 自动压缩图片'
						await this.compressImg(item, compressionRatio)
						if (item.size > this.fileMaxSize) {
							uni.$u.toast('文件' + fileName + '压缩后超出2M')
							return false
						}
					}

					if (item.size < this.fileMinSize) {
						uni.$u.toast('文件' + fileName + '不能小于50KB')
						return false
					}

					this[`fileList`].push({
						...item,
						status: 'uploading',
						message: '上传中'
					})
				}
				for (let i = 0; i < lists.length; i++) {

					const result = await this.uploadFilePromise(lists[i].url)
					// 垃圾回收
					window.URL.revokeObjectURL(lists[i].url)
					console.log('上传结果', result)
					let item = this[`fileList`][fileListLen]
					this[`fileList`].splice(fileListLen, 1, Object.assign(item, {
						status: 'success',
						message: '上传成功',
						url: result
					}))
					fileListLen++
				}
			},
			compressImg(source, compressionRatio) {
				return new Promise((resolve, reject) => {
					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)
				})
			},
			uploadFilePromise(url) {
				let that = this
				return new Promise((resolve, reject) => {
					const uploadTask = uni.uploadFile({
						url: uploadUrl,
						filePath: url,
						name: 'file',
						header: {
							"blade-auth": "Bearer xxxxxxxxxxxxx",
							"tenantCode": tenantCode
						},
						success: (uploadFileRes) => {
							if (uploadFileRes.statusCode === that.Response.OK) {
								const data = JSON.parse(uploadFileRes.data)
								if (data.code === that.Response.OK) {
									console.log('form components upload uploadFilePromise onchange',
										uploadFileRes)
									let fileId = data.data[0].id;
									const sourceUrl = 'https://xxxxxxx/download-document/id/' + fileId;
									that.data.value.push({
										id: fileId,
										name: data.data[0].name,
										source: sourceUrl
									});
									// 添加图片数量
									that.data.count = that.fileList.length
									that.$emit('change', that.data, that.formkey)
									setTimeout(() => {
										resolve(sourceUrl)
									}, 500)
								} else {
									uni.showToast({
										title: data.msg,
										icon: 'none',
										duration: that.ShowToast.DURATION
									});
								}
							} else {
								console.log(uploadFileRes)
								uni.showToast({
									title: '上传失败',
									icon: 'none',
									duration: that.ShowToast.DURATION
								});
							}
						},
						fail(error) {
							// reject(false)
							uni.$u.toast(error.errMsg)
							console.log('图片上传失败', error)
						}
					});

					// uploadTask.onProgressUpdate((res) => {
					// 	console.log('上传进度' + res.progress);
					// 	console.log('已经上传的数据长度' + res.totalBytesSent);
					// 	console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);

					// 	// 测试条件,取消上传任务。
					// 	if (res.progress > 50) {
					// 		uploadTask.abort();
					// 	}
					// });
				})
			},
		}
	} 
<script/>

有问题请添加个人微信:【mengyilingjian】,进群一起技术讨论。添加时请备注来意,谢谢!

有关【uniapp】uniapp 使用uView框架 upload组件压缩图片的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐