HarmonyOS 鸿蒙Next中列表展示大图片缓存问题

HarmonyOS 鸿蒙Next中列表展示大图片缓存问题 我的需求是本地就存了好多照片,照片有几MB 的,最大也有 30 MB 。

想问一下,有没有图片组件,可以缓存这些本地图片的,在加载本地图片的时候,生成一个缩略图存起来,

每次加载的时候,只加载这个缩略图就可以了。

我也尝试了使用 imageknife [https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fimageknifepro](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fimageknifepro)

发现,他也只是遇到网络图片才缓存 。

所以, 我这个需求有没有现成的可以用呢?


更多关于HarmonyOS 鸿蒙Next中列表展示大图片缓存问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

开发者您好,可以采取以下方式解决:获取图片和视频缩略图

更多关于HarmonyOS 鸿蒙Next中列表展示大图片缓存问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


结合内存缓存(LRUCache)与磁盘缓存,通过压缩技术生成低分辨率缩略图

参考代码如下

通过异步任务处理大图压缩:

import { LRUCacheUtil } from '../utils/LRUCacheUtil'
import fs from '@kit.FileKit'

async function generateThumbnail(originalPath: string) {
  // 1. 检查缓存是否存在
  const cache = LRUCacheUtil.getInstance()
  if (cache.contains(originalPath)) {
    return cache.getCache(originalPath)
  }

  // 2. 生成缩略图
  const pixelMap = await image.createImageSource(originalPath)
                     .createPixelMap(options)
  
  // 3. 缓存到内存
  cache.putCache(originalPath, pixelMap)
  
  // 4. 持久化到磁盘(可选)
  const cachePath = getCacheFilePath(originalPath)
  await fs.copy(originalPath, cachePath)
  
  return pixelMap
}

在List组件中按需加载:

@Component
struct ThumbnailImage {
  private originalUri: string = ''

  build() {
    Image(this.loadImage())
      .width(200)
      .height(200)
      .interpolation(ImageInterpolation.High) // 高质量缩放
  }

  async loadImage() {
    return await generateThumbnail(this.originalUri)
  }
}

楼主存在本地的时候就可以对图片进行压缩

参考实现步骤:
1.将图片进行压缩:通过packToFile完成重编码,实现图片压缩功能。

const imagePackerApi: image.ImagePacker = image.createImagePacker();
imagePackerApi.packToFile(imageSource, fileLoc.fd, packOpts).then(() => {
    console.info('Succeeded in packing the image to file.');
})

2.预览压缩后的图片:通过fs.listFileSync获取沙箱中的文件列表,使用path得到文件的URI,存储至fileList中,通过Image进行展示。

let sandboxPath = this.context.getApplicationContext().filesDir;
let filenames = fs.listFileSync(sandboxPath);
filenames.forEach(async fileName => {
    const fullPath = sandboxPath+ "/" + fileName;
    const uri = fileUri.getUriFromPath(fullPath);
    let stat = fs.statSync(fullPath);
    this.fileList.push(new ImageItem(uri, fileName, this.formatFileSize(stat.size)))
});

参考文档:
图片压缩至不同质量-关键场景示例-拍摄美化类行业实践-场景化知识 - 华为HarmonyOS开发者

您所描述的需求(为本地大图片生成并缓存缩略图以供列表展示)可以通过 getThumbnail 接口实现。getThumbnail 方法就是专门用于为图片和视频资源生成缩略图,以避免直接加载和渲染大尺寸的原文件,从而显著提升列表等场景的加载性能和流畅度。

1. 核心接口:getThumbnail

您可以在获取到媒体资源(PhotoAssetFileAsset)后,调用其 getThumbnail 方法来获取缩略图。该方法有以下几种形式:

  • getThumbnail(callback: AsyncCallback<image.PixelMap>): void
  • getThumbnail(size: image.Size, callback: AsyncCallback<image.PixelMap>): void
  • getThumbnail(size?: image.Size): Promise<image.PixelMap>

关键参数:

  • size (可选): 您可以指定一个 image.Size 对象(例如 { width: 720, height: 720 }) 来设定缩略图的期望尺寸。系统会据此生成高质量且尺寸可控的缩略图。这是解决您大图片加载问题的关键。
  • 返回值/回调值: 返回一个 image.PixelMap 对象,这是一个可以直接传递给 ArkUI 的 Image 组件进行显示的高效像素数据。

2. 实现步骤

您的代码逻辑大致如下:

  1. 获取权限:首先需要申请相册管理模块的读权限 ohos.permission.READ_IMAGEVIDEO
  2. 获取管理对象:通过 photoAccessHelper.getPhotoAccessHelper(context) 获取相册管理模块的实例 (phAccessHelper)。
  3. 查询本地图片:使用 phAccessHelper.getAssets(fetchOption) 接口,配合 predicates 查询条件,获取到本地的图片资源列表 (FetchResult<PhotoAsset>)。
  4. 获取单张图片资源:从列表中获取单个 PhotoAsset 对象。
  5. 生成并获取缩略图:调用 photoAsset.getThumbnail(size) 方法。系统会自动处理缩略图的生成和缓存。对于同一张图片的相同尺寸请求,系统很可能会直接返回已缓存的结果,而无需再次解码原图,这极大地优化了性能。
  6. 显示缩略图:将返回的 PixelMap 对象设置给 Image 组件的 src 属性。
// 以 Promise 模式为例,获取指定尺寸的缩略图
import { dataSharePredicates } from '@kit.ArkData';
import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';

async function loadThumbnailForList(phAccessHelper: photoAccessHelper.PhotoAccessHelper, assetUri: string) {
  // ... 建立查询条件,通过 uri 或其他条件获取到特定的 PhotoAsset ...
  let predicates = new dataSharePredicates.DataSharePredicates();
  // 假设通过 URI 定位某张图片
  // predicates.equalTo(photoAccessHelper.PhotoKeys.URI, assetUri); 

  let fetchOption = {
    fetchColumns: [],
    predicates: predicates
  };
  
  try {
    let fetchResult = await phAccessHelper.getAssets(fetchOption);
    let photoAsset = await fetchResult.getFirstObject();
    
    // 定义您希望在列表中显示的缩略图尺寸
    let thumbnailSize: image.Size = { width: 300, height: 300 }; 
    
    // 关键调用:获取缩略图。系统会处理生成和缓存。
    let pixelMap: image.PixelMap = await photoAsset.getThumbnail(thumbnailSize);
    
    // 此时您可以将 pixelMap 赋值给列表项中的 Image 组件
    // this.listItemImage = pixelMap; 
    console.info('Thumbnail obtained successfully.');
    return pixelMap;
  } catch (err) {
    console.error(`Failed to get thumbnail: ${err.code}, ${err.message}`);
  }
}

如果列表加载的时候只加载缩略图,那直接找ui要一套就行了,当点击事件发生的时候,再去访问单独的那张原图,不然你每次启动的时候都要去对原图生成缩略图,这一步比你加载列表还卡吧

推荐

  1. 使用PhotoView组件+自定义缓存策略 根据搜索结果中提到的PhotoView组件(ohpm install @ohos/photoview),结合其缩放浏览能力,可扩展实现本地缩略图缓存

鸿蒙Next中列表展示大图片的内存优化方案

在鸿蒙Next中,当列表需要展示大图片时,推荐使用Image组件配合PixelMap进行内存优化。

实现步骤

  1. 创建图像源

    • 使用ImageSource.createImageSourceAsync方法创建图像源
  2. 生成PixelMap对象

    • 调用createPixelMap方法生成缩放后的PixelMap对象
  3. 图片尺寸处理

    • 加载大尺寸图片前应先获取原图尺寸
    • 根据列表项显示区域计算合适的采样率
  4. 性能优化

    • 结合LazyForEach实现按需加载
    • 图片移出可视区域后自动释放资源

这种方法可以有效管理内存使用,提升列表滚动性能。

回到顶部