【鸿蒙NEXT】图片选择和图片压缩

这篇具有很好参考价值的文章主要介绍了【鸿蒙NEXT】图片选择和图片压缩。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 相册选择图片

static getPhoto(callback?: (result: string) => void) {
    try {
      let photoSelectOptions = new picker.PhotoSelectOptions();
      photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
      photoSelectOptions.maxSelectNumber = 1;
      let photoPicker = new picker.PhotoViewPicker();
      photoPicker.select(photoSelectOptions).then((photoSelectResult: picker.PhotoSelectResult) => {
        QDLogUtils.debug('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));
        let arr = photoSelectResult['photoUris']
        let uri = arr[0]
        try {
          let file: fileIo.File = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
          let buffer = new ArrayBuffer(1024 * 1024 * 10); //100M 10485760
          let fileFd = file.fd
          let readLen = fileIo.readSync(fileFd, buffer); //1347190
          buffer = buffer.slice(0, readLen)
          fileIo.closeSync(fileFd);
          QDImageCompressionUtils.imageCompression(buffer, (res) => {
            let content = QDStringUtils.base64Encode(new Uint8Array(res))
            if (callback) callback(content)
          })
        } catch (err) {
          QDLogUtils.error(`photoPicker.select err = ${err}`)
          if (callback) callback("")
        }
      }).catch((err: BusinessError) => {
        QDLogUtils.error('PhotoViewPicker.select failed with err: ' + JSON.stringify(err));
        if (callback) callback("")
      });
    } catch (error) {
      let err: BusinessError = error as BusinessError;
      QDLogUtils.error('PhotoViewPicker failed with err: ' + JSON.stringify(err));
      if (callback) callback("")
    }
  }

1. 图片压缩 QDImageCompressionUtils

import { BusinessError } from '@ohos.base';
import fs from '@ohos.file.fs'; // 导入文件管理模块
import image from '@ohos.multimedia.image'; // 导入Image模块
import QDLogUtils from './QDLogUtils';

/**
 *
 * */
class QDImageCompressionUtils {
  /**
   * 压缩图片,默认100KB
   * @param buffer 图片数据
   * @param maxCompressedImageSize 指定图片的压缩目标大小,单位kb
   */
  imageCompression(buffer: ArrayBuffer, callback: (buffer: ArrayBuffer) => void, maxCompressedImageSize: number = 100): void {
    // 创建ImageSource实例
    const imageSource: image.ImageSource = image.createImageSource(buffer);
    // 设置解码参数DecodingOptions,解码获取PixelMap图片对象。
    let decodingOptions: image.DecodingOptions = {
      editable: true, // 是否可编辑。当取值为false时,图片不可二次编辑,如crop等操作将失败。
      desiredPixelFormat: 3, // 解码的像素格式。3表示RGBA_8888。
    }
    // 创建pixelMap
    imageSource.createPixelMap(decodingOptions).then((originalPixelMap: image.PixelMap) => {
      // 压缩图片
      this.compressedImage(originalPixelMap, maxCompressedImageSize).then((buffer: ArrayBuffer) => {
        if (callback) {
          callback(buffer)
        }
      })
    }).catch((err: BusinessError) => {
      QDLogUtils.error(`Failed to create PixelMap with error message: ${err.message}, error code: ${err.code}`);
    });
  }

  /**
   * 图片压缩,保存
   * @param sourcePixelMap:原始待压缩图片的PixelMap对象
   * @param maxCompressedImageSize:指定图片的压缩目标大小,单位kb
   * @returns compressedImageInfo:返回最终压缩后的图片信息
   */
  private async compressedImage(sourcePixelMap: PixelMap, maxCompressedImageSize: number): Promise<ArrayBuffer> {
    // 创建图像编码ImagePacker对象
    let imagePackerApi = image.createImagePacker();
    // 定义图片质量参数
    let imageQuality = 0;
    // 设置编码输出流和编码参数。图片质量参数quality范围0-100。
    let packOpts: image.PackingOption = { format: "image/jpeg", quality: imageQuality };
    // 通过PixelMap进行编码。compressedImageData为打包获取到的图片文件流。
    let compressedImageData: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts);
    // 压缩目标图像字节长度
    let maxCompressedImageByte = maxCompressedImageSize * 1024;
    // TODO 知识点:图片压缩。先判断设置图片质量参数quality为0时,packing能压缩到的图片最小字节大小是否满足指定的图片压缩大小。如果满足,则使用packing方式二分查找最接近指定图片压缩目标大小的quality来压缩图片。如果不满足,则使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据。
    if (maxCompressedImageByte > compressedImageData.byteLength) {
      // 使用packing二分压缩获取图片文件流
      compressedImageData = await this.packingImage(compressedImageData, sourcePixelMap, imageQuality, maxCompressedImageByte);
    } else {
      // 使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据
      let imageScale = 1; // 定义图片宽高的缩放倍数,1表示原比例。
      let reduceScale = 0.4; // 图片缩小倍数
      // 判断压缩后的图片大小是否大于指定图片的压缩目标大小,如果大于,继续降低缩放倍数压缩。
      while (compressedImageData.byteLength > maxCompressedImageByte) {
        if (imageScale > 0) {
          // 性能知识点: 由于scale会直接修改图片PixelMap数据,所以不适用二分查找scale缩放倍数。这里采用循环递减0.4倍缩放图片,来查找确定最适
          // 合的缩放倍数。如果对图片压缩质量要求不高,建议调高每次递减的缩放倍数reduceScale,减少循环,提升scale压缩性能。
          imageScale = imageScale - reduceScale; // 每次缩放倍数减0.4
          // 使用scale对图片进行缩放
          await sourcePixelMap.scale(imageScale, imageScale);
          // packing压缩
          compressedImageData = await this.packing(sourcePixelMap, imageQuality);
        } else {
          // imageScale缩放小于等于0时,没有意义,结束压缩。这里不考虑图片缩放倍数小于reduceScale的情况。
          break;
        }
      }
    }
    // 保存图片,返回压缩后的图片信息。
    return compressedImageData
  }

  /**
   * packing压缩
   * @param sourcePixelMap:原始待压缩图片的PixelMap
   * @param imageQuality:图片质量参数
   * @returns data:返回压缩后的图片数据
   */
  private async packing(sourcePixelMap: PixelMap, imageQuality: number): Promise<ArrayBuffer> {
    let imagePackerApi = image.createImagePacker();
    let packOpts: image.PackingOption = { format: "image/jpeg", quality: imageQuality };
    let data: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts);
    return data;
  }

  /**
   * packing二分方式循环压缩
   * @param compressedImageData:图片压缩的ArrayBuffer
   * @param sourcePixelMap:原始待压缩图片的PixelMap
   * @param imageQuality:图片质量参数
   * @param maxCompressedImageByte:压缩目标图像字节长度
   * @returns compressedImageData:返回二分packing压缩后的图片数据
   */
  private async packingImage(compressedImageData: ArrayBuffer, sourcePixelMap: PixelMap, imageQuality: number, maxCompressedImageByte: number): Promise<ArrayBuffer> {
    // 图片质量参数范围为0-100,这里以10为最小二分单位创建用于packing二分图片质量参数的数组。
    let packingArray: number[] = [];
    let dichotomyAccuracy = 10;
    // 性能知识点: 如果对图片压缩质量要求不高,建议调高最小二分单位dichotomyAccuracy,减少循环,提升packing压缩性能。
    for (let i = 0; i <= 100; i += dichotomyAccuracy) {
      packingArray.push(i);
    }
    let left = 0; // 定义二分搜索范围的左边界
    let right = packingArray.length - 1; // 定义二分搜索范围的右边界
    // 二分压缩图片
    while (left <= right) {
      let mid = Math.floor((left + right) / 2); // 定义二分搜索范围的中间位置
      imageQuality = packingArray[mid]; // 获取二分中间位置的图片质量值
      // 根据传入的图片质量参数进行packing压缩,返回压缩后的图片文件流数据。
      compressedImageData = await this.packing(sourcePixelMap, imageQuality);
      // 判断查找一个尽可能接近但不超过压缩目标的压缩大小
      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]);
        // 判断用下一次图片质量参数(mid+1)压缩的图片大小是否大于指定图片的压缩目标大小。如果大于,说明当前图片质量参数(mid)压缩出来的
        // 图片大小最接近指定图片的压缩目标大小。传入当前图片质量参数mid,得到最终目标图片压缩数据。
        if (compressedImageData.byteLength > maxCompressedImageByte) {
          compressedImageData = await this.packing(sourcePixelMap, packingArray[mid]);
          break;
        }
      } else {
        // 目标值不在当前范围的右半部分,将搜索范围的右边界向左移动,以缩小搜索范围并继续在下一次迭代中查找左半部分。
        right = mid - 1;
      }
    }
    return compressedImageData;
  }

  /**
   * 图片保存
   * @param compressedImageData:压缩后的图片数据
   * @returns compressedImageInfo:返回压缩后的图片信息
   */
  private async saveImage(compressedImageData: ArrayBuffer): Promise<CompressedImageInfo> {
    let context: Context = getContext();
    // 定义要保存的压缩图片uri。afterCompressiona.jpeg表示压缩后的图片。
    let compressedImageUri: string = context.filesDir + '/' + 'afterCompressiona.jpeg';
    try {
      let res = fs.accessSync(compressedImageUri);
      if (res) {
        // 如果图片afterCompressiona.jpeg已存在,则删除
        fs.unlinkSync(compressedImageUri);
      }
    } catch (err) {
      QDLogUtils.error(`AccessSync failed with error message: ${err.message}, error code: ${err.code}`);
    }
    // TODO 知识点:保存图片。获取最终图片压缩数据compressedImageData,保存图片。
    // 压缩图片数据写入文件
    let file: fs.File = fs.openSync(compressedImageUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    fs.writeSync(file.fd, compressedImageData);
    fs.closeSync(file);
    // 获取压缩图片信息
    let compressedImageInfo: CompressedImageInfo = new CompressedImageInfo();
    compressedImageInfo.imageUri = compressedImageUri;
    compressedImageInfo.imageByteLength = compressedImageData.byteLength;
    return compressedImageInfo;
  }
}

export default new QDImageCompressionUtils()


// 压缩后的图片信息类,用于刷新显示压缩后的图片和图片字节长度
class CompressedImageInfo {
  imageUri: string = ""; // 压缩后图片保存位置的uri
  imageByteLength: number = 0; // 压缩后图片字节长度
}

文章来源地址https://www.toymoban.com/news/detail-846882.html

到了这里,关于【鸿蒙NEXT】图片选择和图片压缩的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 华为harmonyos4.0鸿蒙4.0安装谷歌服务框架Play商店,解决从服务器检索信息时出错

    8月4号华为手机发布了全新的harmonyos4.0鸿蒙4.0系统,很多人需要问还是不是支持谷歌服务框架?那么答案是肯定的,它和鸿蒙3是一样的,一样的操作,一样的支持安装谷歌服务框架,安装Google play商店。测试机型,Mate30,Mate40,Mate50,P50,P60,华为的几款折叠屏xs,x3,这几款测试都是

    2024年02月13日
    浏览(36)
  • 鸿蒙4.0和鸿蒙Next有什么区别?

    鸿蒙4.0和鸿蒙Next有什么区别呢? 首先,我们来说说二者的共性: 鸿蒙Next基于api10 而鸿蒙4.0基于api9,但基本语法都是一样的,故值得提醒大家的是若早点学习鸿蒙4.0,那么下半年当华为的鸿蒙Next正式发布后就可以快速分到第一杯羮。 鸿蒙4.0和鸿蒙Next的区别: 鸿蒙4.0和鸿蒙

    2024年04月23日
    浏览(39)
  • HarmonyOS NEXT鸿蒙星河版发布

    1月18日,在深圳举行的“鸿蒙生态千帆启航仪式”上,华为常务董事、终端BG CEO余承东宣布HarmonyOS NEXT鸿蒙星河版面向开发者开放申请。鸿蒙星河版将实现原生精致、原生易用、原生流畅、原生安全、原生智能、原生互联6大极致原生体验。 并且,华为在 1 月 15 日开启了Harm

    2024年01月19日
    浏览(31)
  • 【鸿蒙4.0】harmonyos Day 04

    文字按钮 自定义按钮,在Button内嵌套其他组件

    2024年01月22日
    浏览(44)
  • HarmonyOS Next 自定义安全键盘案例_鸿蒙next 自定义键盘(1)

    // 切换大写字母键盘 this.curKeyboardType = EKeyboardType.UPPERCASE; this.items = upperCaseKeyData; } else { // 切换小写字母键盘 this.curKeyboardType = EKeyboardType.LOWERCASE; this.items = lowerCaseKeyData; } break; // 切换特殊字符键盘 case EKeyType.SPECIAL: if (this.curKeyboardType !== EKeyboardType.SPECIAL) { this.curKeyboardType =

    2024年04月17日
    浏览(47)
  • 【鸿蒙4.0】详解harmonyos开发语言ArkTS

    如官方文档所描述,ArkTS是基于Javascript以及Typescript的生态上做了进一步的扩展,进一步的提高开发效率,使用过低代码开发的老师应该会有一种感觉ArkTS像低代码一样更简洁,几行代码就能实现一个功能。以开发者的角度肯定是越简洁越好。下面来浅浅的了解一下js,ts以及

    2024年01月22日
    浏览(51)
  • 鸿蒙终于不套壳了?纯血 HarmonyOS NEXT 即将到来

    对于移动开发者来说,特别是 Android 开发而言,鸿蒙是不是套壳 Android 一直是一个「热门」话题,类似的问题一直是知乎的「热点流量」之一,特别是每次鸿蒙发布新版本之后,都会有「套娃式」的问题出现。 例如最近 HDC 刚发布了鸿蒙 4.0 ,但是问题已经提到了 6.0 ,不过也

    2024年02月07日
    浏览(33)
  • MediaBox音视频终端SDK已适配鸿蒙星河版(HarmonyOS NEXT)

    2024年1月,HarmonyOS NEXT 鸿蒙星河版系统开发者预览版开放申请,该系统将只能安装为鸿蒙开发的原生应用,而不再兼容安卓应用。对此,阿里云MediaBox音视频终端SDK产品已实现功能的鸿蒙化迁移和重构,全面适配鸿蒙系统HarmonyOS NEXT系统。 当前, 阿里云播放器SDK预览版已面

    2024年03月22日
    浏览(48)
  • 【鸿蒙千帆起】《钢岚》成为首款基于 HarmonyOS NEXT 开发的战棋新游

    近日,紫龙游戏旗下 BlackJack 工作室全新战棋旗舰作品《钢岚》在华为游戏中心首发上线,并宣布《钢岚》完成鸿蒙原生应用开发,成为基于 HarmonyOS NEXT 开发的首款战棋新游,不但进一步丰富了鸿蒙生态战棋品类游戏内容,也是鸿蒙生态游戏内容建设的重要进展,为鸿蒙生态

    2024年02月03日
    浏览(43)
  • 【开源-土拨鼠充电系统】鸿蒙 HarmonyOS 4.0 App+微信小程序+云平台

    ✨本人自己开发的开源项目:土拨鼠充电系统 ✨踩坑不易,还希望各位大佬支持一下,在 Gitee 或 GitHub 给我点个  Start  ⭐⭐👍👍 ✍Gitee开源项目地址 👉: https://gitee.com/cheinlu/groundhog-charging-system ✍GitHub开源项目地址 👉 :https://github.com/cheinlu/groundhog-charging-system 土拨鼠开

    2024年03月26日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包