超大分辨率图片压缩时应用Crash
=================

#### 问题现象

超大分辨率（30648X12480）图片可在图库中正常显示，但压缩时应用直接Crash，如何实现超大分辨率的图片压缩？

#### 背景知识

图片压缩是指把原始图片处理为指定大小以内的图片。目前图片压缩支持jpeg、webp、png格式。超大分辨率图片在压缩过程中通过[packToData](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-imagepacker#packtodata13)编码存在[内存泄漏](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-arkts-memory-leak-analysis)问题，需要在解码前对图片进行优化处理，通过[DecodingOptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-i#decodingoptions7)中的desiredSize属性提前设置缩小后的分辨率，可以提高压缩效率并且避免内存溢出。

#### 问题定位

1.  根据如下代码进行图片压缩：
    
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
    resourceMgr.getRawFileContent('XXXXXX.jpg').then((fileData: Uint8Array) => {
          const buffer = fileData.buffer.slice(0);
          const imageSource: image.ImageSource = image.createImageSource(buffer);
          const decodingOptions: image.DecodingOptions = {
            editable: true
          }
          imageSource.createPixelMap(decodingOptions).then((originalPixelMap: image.PixelMap) => {
             const imagePackerApi = image.createImagePacker();
             const packOpts: image.PackingOption = { format: 'image/webp', quality: 1 };
             let compressedImageData: ArrayBuffer = await imagePackerApi.packToData(sourcePixelMap, packOpts);
          })
    })
    
2.  压缩小分辨率图片时可以正常压缩图片，功能无异常；
3.  压缩超大分辨率（30648X12480）图片，当[PackingOption](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-i#packingoption)参数中的quality设置为0时，输出最低质量图片，应用仍然Crash；
4.  分析日志，写入数据超出最大值“write data:\[29144176\] out of max size:\[26214400\].”；
5.  [DevEco Profiler工具](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-profiler)分析，packToData压缩图片时内存溢出。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/1f/v3/M052Yb-YRZqvLpQrfKSk6Q/zh-cn_image_0000002658791873.png?HW-CC-KV=V1&HW-CC-Date=20260709T134240Z&HW-CC-Expire=86400&HW-CC-Sign=618FF5188E717D23A3A81229E2A2EADBEC01C0581D3A71471873644C34D7B890 "点击放大")
    
    PrivateDirty持续增长，存在内存泄漏风险。
    

#### 分析结论

超大分辨率图片通过packToData编码时存在内存溢出问题，需要在解码前对图片进行优化处理，通过desiredSize属性提前设置缩小后的分辨率，避免内存溢出且提高压缩效率。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/1d/v3/MXoCPiNzSzqfHPDHMouZgQ/zh-cn_image_0000002628552504.png?HW-CC-KV=V1&HW-CC-Date=20260709T134240Z&HW-CC-Expire=86400&HW-CC-Sign=00958C60AFAFAFF138C69FEDC8CD94E99EC9780AB928D3B3896ACFFA0FA2ADFC "点击放大")

#### 修改建议

使用packToData对超大分辨率图片进行编码时需要设置desiredSize属性提前缩小图片分辨率，代码示例如下：

import { common } from '@kit.AbilityKit';
import { resourceManager } from '@kit.LocalizationKit';
import { image } from '@kit.ImageKit';
import { util } from '@kit.ArkTS';


@Entry
@Component
struct Index {
  @State base64Image: string = '';


  aboutToAppear(): void {
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
    // startIcon.png仅供参考使用，开发者可替换为实际使用图片
    resourceMgr.getMediaContent($r('app.media.startIcon').id).then((fileData: Uint8Array) => {
      const buffer = fileData.buffer.slice(0);
      const imageSource: image.ImageSource = image.createImageSource(buffer);
      const decodingOptions: image.DecodingOptions = {
        editable: true,
        // desiredSize属性设置为缩小后的分辨率
        desiredSize: { width: 1000, height: 1000 }
      };
      imageSource.createPixelMap(decodingOptions).then(async (originalPixelMap: image.PixelMap) => {
        const imagePackerApi = image.createImagePacker();
        const packOpts: image.PackingOption = { format: 'image/webp', quality: 1 };
        let compressedImageData: ArrayBuffer = await imagePackerApi.packToData(originalPixelMap, packOpts);
        // 转为base64编码，用来展示图片
        let base64Helper = new util.Base64Helper();
        this.base64Image =
          'data:image/webp;base64,' + base64Helper.encodeToStringSync(new Uint8Array(compressedImageData));
      });
    });
  }


  build() {
    Column() {
      Image(this.base64Image)
        .width(200)
        .height(200)
    }
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .height('100%')
    .width('100%')
  }
}