一、鸿蒙实现处理-压缩上传整体代码处理逻辑
- 转沙箱
- 压缩
- 获取凭证并上传文件
- 文件准备(拿到文件流)
- 获取上传凭证(调接口1拿到file_name和upload_url)
- 执行文件上传(向阶段2拿到的upload_url上传文件)
- 更新列表数据 (将阶段2获取到的file_name更新进去,最后提交表单时传参)
- 提交表单
// 处理视频/图片(转沙箱、压缩、调接口获取url、上传到OSS)// index不是undefined时:修改async handleFile(messageInfo: MessageInfo, index?: number) {loadingInstance.show()if (this.videoFlag) {// 1视频处理// 1)转沙箱const file = uriToSandBox(messageInfo.sourceFilePath);// 2)压缩const fileUri = await this.videoToCompress(file.fileUri).catch(() => {loadingInstance.hide()showToast('视频压缩失败');});let stat = fs.statSync(fileUri as string);if (stat.size / 1024 / 1024 > 50) {loadingInstance.hide()showToast('请拍摄50M以内的视频');return;}// 3)获取上传凭证并上传fileUri && this.fileUpload(fileUri, messageInfo, index)} else {// 2图片处理const file = uriToSandBox(messageInfo.sourceFilePath);// 1)转沙箱并压缩const fileUri = await this.transferFile(messageInfo.sourceFilePath).catch(() => {loadingInstance.hide()});// 2)获取上传凭证并上传fileUri && this.fileUpload(fileUri, messageInfo, index)}}
二、压缩部分
实现思路:
图片压缩
packing二分方式循环压缩
视频压缩
三种方案
代码:
// 优先压缩图片质量
async qualityPriorityCompress(sourcePixelMap: image.PixelMap, maxCompressedImageSize: number) {let compressedImageData: ArrayBuffer =await this.packing(sourcePixelMap, IMAGE_QUALITY_ZERO, this.afterCompressFmt);// 先判断图片质量参数设置最低0能否满足目标大小。如果能满足目标大小,则直接使用packing二分图片质量。如果不满足,则质量参数固定为0,进行scale尺寸压缩if (compressedImageData.byteLength <= maxCompressedImageSize * BYTE_CONVERSION) {// 满足目标大小,直接使用packing二分await this.packingImage(sourcePixelMap, compressedImageData, maxCompressedImageSize * BYTE_CONVERSION);} else {// 不满足目标大小,质量参数设置为0,再进行scale尺寸压缩await this.scalePriorityCompress(sourcePixelMap, maxCompressedImageSize, IMAGE_QUALITY_ZERO);}// 更新显示压缩后的图片格式this.showCompressFormat = this.afterCompressFmt;
}// packing二分方式循环压缩
async packingImage(sourcePixelMap: image.PixelMap, compressedImageData: ArrayBuffer, maxCompressedImageByte: number) {let imageQuality: number = 0;const DICHOTOMY_ACCURACY = this.minBisectUnit;// 图片质量参数范围为0-100,这里以minBisectUnit为最小二分单位创建用于packing二分图片质量参数的数组。const packingArray: number[] = [];// 性能知识点: 如果对图片压缩质量要求不高,建议调高minBisectUnit(对应‘packing最小二分单位’),减少循环,提升packing压缩性能。for (let i = 0; i <= 100; i += DICHOTOMY_ACCURACY) {packingArray.push(i);}let left = 0; // 定义二分搜索范围的左边界let right = packingArray.length - 1; // 定义二分搜索范围的右边界const AFTER_COMPRESS_FMT = this.afterCompressFmt;// 二分压缩图片while (left <= right) {const mid = Math.floor((left + right) / 2); // 定义二分搜索范围的中间位置imageQuality = packingArray[mid]; // 获取二分中间位置的图片质量值// 根据传入的图片质量参数进行packing压缩,返回压缩后的图片文件流数据。compressedImageData = await this.packing(sourcePixelMap, imageQuality, AFTER_COMPRESS_FMT);// 判断查找一个尽可能接近但不超过压缩目标的压缩大小if (compressedImageData.byteLength <= maxCompressedImageByte) {// 二分目标值在右半边,继续在更高的图片质量参数(即mid + 1)中搜索left = mid + 1;// 判断mid是否已经二分到最后,如果二分完了,退出if (mid === packingArray.length - 1) {break;}// 获取下一次二分的图片质量参数(mid+1)压缩的图片文件流数据compressedImageData = await this.packing(sourcePixelMap, packingArray[mid + 1], AFTER_COMPRESS_FMT);// 判断用下一次图片质量参数(mid+1)压缩的图片大小是否大于指定图片的压缩目标大小。如果大于,说明当前图片质量参数(mid)压缩出来的// 图片大小最接近指定图片的压缩目标大小。传入当前图片质量参数mid,得到最终目标图片压缩数据。if (compressedImageData.byteLength > maxCompressedImageByte) {compressedImageData = await this.packing(sourcePixelMap, packingArray[mid], AFTER_COMPRESS_FMT);break;}} else {// 目标值不在当前范围的右半部分,将搜索范围的右边界向左移动,以缩小搜索范围并继续在下一次迭代中查找左半部分。right = mid - 1;}}// ...
}
三、OSS上传文件部分
方案图:
代码:
鸿蒙环境服务端签名直传_对象存储(OSS)-阿里云帮助中心
// 获取上传凭证并上传// index不是undefined时:修改private fileUpload = async (fileUrl: string, messageInfo: MessageInfo, index?: number) => {let fileInfo: fs.File | null = null;try {// 阶段1:文件准备fileInfo = await fs.open(fileUrl, fs.OpenMode.READ_ONLY);const fileStat = await fs.stat(fileInfo.fd);const fileData = new ArrayBuffer(fileStat.size);await fs.read(fileInfo.fd, fileData);// 阶段2:获取上传凭证(调接口1拿到file_name和upload_url)const fileType = this.videoFlag ? 'mp4' : 'jpg';const uploadInfo = await this.getUploadCredentials(fileType);// 阶段3:执行文件上传(向阶段2拿到的upload_url上传文件)await this.uploadFileToOSS(uploadInfo.upload_url, fileData, fileStat.size)// 阶段4:更新列表数据 (将阶段2获取到的file_name更新进去,最后提交表单时传参)this.updateMediaLists(uploadInfo.file_name, messageInfo, index);return true;} catch (error) {showToast('文件上传失败,请重新上传')return false;} finally {// 确保资源释放if (fileInfo?.fd) {await fs.close(fileInfo.fd);}loadingInstance.hide();}};