项目场景:

本博在做一款安卓+IOS+小程序的项目的时候。产品有一个业务是app、小程序需要分享小程序链接到微信中。但是分享的时候有些时候能分享出去,但是有时候又分享不出去,经过对所有参数的排查定位到imageUrl参数。


问题描述

官方文档上描述type=图文的时候,推荐使用20kb以下的图片(实测可以分享100KB以下的图片)


解决方案:

Tips: 这里面在图片上传的时候有个陷阱,应为图片虽然经过压缩,但是图片的名称不会因为压缩图片而改变。如果直接传入oss之后可能会导致图片覆盖问题(或者传不上去),在这个问题上可以分为前端和后端两种解决方法,前端的话可以通过uni.savefile去进行保存后再上传,后台的话更简单一些,生成一个唯一的图片名称就可以了!

以下是封装的一个可以同时适配小程序和app的函数,只调用主函数compressImg2Share传入图片的Url。原理就是递归的压缩图片,这样可以达到的效果是将图片压缩到100KB以下,但是无法准确的压缩到100KB,实现原理是通过压缩图片的宽高以达到压缩图片大小的目的,因为图片大小是像素点的色彩复杂程度决定的,所以无法通过宽高去线性的压缩到目标大小。

如果想要精确的压缩到100KB,可以通过增大compressRate参数改变压缩的细腻度(但是这样会增加应用的负载)。

// compress.js 压缩文件

/**
 * 获取文件基本信息
 * @param {String} url 文件地址
 */
export function getFileInfoo(url) {
	return new Promise((resolve, reject) => {
		if (!url) {
			return reject('url is empty')
		}
		uni.downloadFile({
			url: url,
			success: (dRes) => {
				if (dRes.statusCode !== 200) {
					reject('downloadFile Error: 下载失败')
					return
				}
				const tempPath = dRes.tempFilePath
				// 获取源文件名称和后缀
				const fileName = url.substring(url.lastIndexOf('/') + 1)

				const file = {
					width: 0,
					height: 0,
					size: 0,
					temp: tempPath,
					name: fileName
				}
				const checkReturn = () => {
					if (!file.width || !file.height || !file.size) return;
					resolve(file)
				}
				uni.getFileInfo({
					filePath: tempPath,
					success: (res) => {
						if (!res.size) reject('getFileInfo Error: 文件大小错误')
						file.size = res.size / 1024;
						checkReturn()
					}
				})
				uni.getImageInfo({
					src: tempPath,
					success: (res) => {
						if (!res.width || !res.height) reject('getImageInfo Error: 文件宽高获取失败')
						file.width = res.width;
						file.height = res.height;
						checkReturn()
					}
				})
			},
			fail: (err) => {
				console.log("下载文件失败:", err)
			}
		})
	})
}

/**
 * 递归压缩图片到指定大小 - 可省略
 * @param {string} url - 图片 URL
 * @param {number} targetSize - 目标文件大小 (KB),默认 20KB
 * @returns {Promise<string>} - 压缩后的图片 URL
 */
export function compressImageRecursive(url, targetSize = 20) {
	return new Promise((resolve, reject) => {
		if (!url) {
			return reject('url is empty')
		}

		getFileInfoo(url).then(res => {
			console.log(`原始图片信息:`, {
				width: res.width,
				height: res.height,
				size: res.size.toFixed(2) + 'KB'
			});

			if (res.size <= targetSize) {
				resolve(url)
				return
			}

			compressLoop(res.temp, res.width, res.height, res.size, targetSize, url, 1)
				.then(resolve)
				.catch(reject)
		}).catch(reject)
	})
}

/** 循环压缩图片
 * @param {String} tempPath Uniapp临时地址
 * @param {Number} width 当前宽度
 * @param {Number} height 当前高度
 * @param {Number} size 当前大小 - KB
 * @param {Number} targetSize 目标大小
 * @param {Number} count 压缩次数
 */
function compressLoop(tempPath, width, height, size, targetSize, count) {
	return new Promise((resolve, reject) => {
		const compressRate = 0.9
		const newWidth = Math.floor(width * compressRate)
		const newHeight = Math.floor(height * compressRate)

		console.log(`第${count}次压缩:`, {
			width: newWidth,
			height: newHeight,
			currentSize: size.toFixed(2) + 'KB',
			targetSize: targetSize + 'KB'
		})

		if (newWidth < 50 || newHeight < 50) {
			console.warn('已达到压缩极限')
			resolve(tempPath)
			return
		}

		uni.compressImage({
			src: tempPath,
			compressedWidth: newWidth,
			compressedHeight: newHeight,
			success: async (resc) => {
				const newTempPath = resc.tempFilePath

				uni.getFileInfo({
					filePath: newTempPath,
					success: async (fileRes) => {
						const newSize = fileRes.size / 1024

						console.log(`压缩后:${newSize.toFixed(2)}KB`)

						if (newSize > targetSize) {
							try {
								const result = await compressLoop(
									newTempPath,
									newWidth,
									newHeight,
									newSize,
									targetSize,
									count + 1
								)
								resolve(result)
							} catch (err) {
								reject(err)
							}
						} else {
							resolve({
								temp:newTempPath,
								size: newSize,
								width: newWidth,
								height: newHeight
							})
						}
					},
					fail: (err) => {
						reject('获取文件大小失败:' + JSON.stringify(err))
					}
				})
			},
			fail: (err) => {
				reject('compressImage Error: ' + JSON.stringify(err))
			}
		})
	})
}

/**
 * 分享到微信,图片压缩方法 - 主方法
 * @param {String} url 图片地址
 */
export function compressImg2Share(url) {
	return new Promise((resolve, reject) => {
		if (!url) {
			return reject('url is empty')
		}
		getFileInfoo(url).then(res => {
			// 已有缓存
			const xccShareImgCache = storage.get(res.name)
			if (xccShareImgCache) {
				resolve(xccShareImgCache)
			} else {
				// 未发现缓存
				const size = res.size / 20
				const changeSize = {
					width: Math.floor(res.width / size),
					height: Math.floor(res.height / size),
					size: Math.floor(size * 1024)
				}
				compressLoop(res.temp, res.width, res.height, res.size, 101, url, 1)
					.then(async (resc) => {
						const tempFile = resc.temp;
						console.log("tempFile", tempFile)
                        // 上传到线上问题 - start 改成自己的上传逻辑
						const upload = await doUploadsReady({
							model: 'image',
							name: res.name,
							path: tempFile,
							size: resc.size
						})
						if (upload) {
							const {
								data
							} = upload;
							uni.uploadFile({
								url: data.host,
								filePath: tempFile,
								name: 'file',
								formData: {
									key: data.key,
									policy: data.policy,
									OSSAccessKeyId: data.ossAccessKeyId,
									signature: data.signature
								},
								success: async (resu) => {
									if (resu.statusCode === 204) {
										const filePath = data.host + '/' + data.key;
										console.log("filePath", filePath)
										// 设置缓存
										storage.set(res.name, filePath)
										resolve(filePath)
									}
								},
								fail: (err) => {}
							});
						}
                        // 上传到线上问题 - start
					})

			}
		}).catch(res => {
			console.log("Error", res)
		})
	})
}

更多推荐