鸿蒙OSS文件(视频/图片)压缩上传组件-能够增删改查

一、鸿蒙实现处理-压缩上传整体代码处理逻辑

  1. 转沙箱
  2. 压缩
  3. 获取凭证并上传文件
    1. 文件准备(拿到文件流)
    2. 获取上传凭证(调接口1拿到file_name和upload_url)
    3. 执行文件上传(向阶段2拿到的upload_url上传文件)
    4. 更新列表数据 (将阶段2获取到的file_name更新进去,最后提交表单时传参)
  4. 提交表单
复制代码
 // 处理视频/图片(转沙箱、压缩、调接口获取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();
    }
  };
相关推荐
吃瓜群众i1 小时前
理解Javascript闭包
前端·javascript
安大桃子1 小时前
Mapbox GL + Deck.gl 三维实战:Mapbox 加载 Tileset3D 倾斜摄影模型
前端·webgl
yede1 小时前
多行文本省略号显示,更多按钮展开全部
前端
就是我1 小时前
React 应用性能优化实战
前端·react.js·性能优化
G扇子1 小时前
深入解析XSS攻击:从原理到防御的全方位指南
前端·安全
snakeshe10101 小时前
入解析React性能优化策略:eagerState的工作原理
前端
六边形6661 小时前
Vue中的 ref、toRef 和 toRefs 有什么区别
前端·vue.js·面试
kovli1 小时前
红宝书第十八讲:详解JavaScript的async/await与错误处理
前端·javascript
前端付豪1 小时前
🚀 React 应用国际化实战:深入掌握 react-i18next 的高级用法
前端·react.js·架构
代码小学僧1 小时前
使用 Cloudflare workers 做一个定时发送消息的飞书机器人
前端·云原生·serverless