HarmonyOS鸿蒙Next中选择相册图片后,怎么获取图片的尺寸信息呢?

HarmonyOS鸿蒙Next中选择相册图片后,怎么获取图片的尺寸信息呢? PhotoViewPicker和PhotoPickerComponent返回的uri;

image.createImageSource(uri).getImageInfo

后者返回的uri直接报错:

前者没有任何日志

7 回复
import { photoAccessHelper } from '@kit.MediaLibraryKit';

let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 1;
let photoPicker = new photoAccessHelper.PhotoViewPicker();
photoPicker.select(photoSelectOptions, async (
    err: BusinessError, photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
    if (err) {
        Logger.error('PhotoViewPicker.select failed with err: ' + JSON.stringify(err));
        return;
    }
    Logger.info('PhotoViewPicker.select successfully, ' +
        'photoSelectResult uri: ' + JSON.stringify(photoSelectResult));
    this.uris = photoSelectResult.photoUris;
    Logger.info('uri: ' + this.uris);
    try {
        let file = fileIo.openSync(this.uris[0], fileIo.OpenMode.READ_ONLY);
        Logger.info('file fd: ' + file.fd);
        let inputBuffer = new ArrayBuffer(CommonConstants.ARRAY_BUFFER_SIZE);
        let readLen = fileIo.readSync(file.fd, inputBuffer);
        Logger.info('readSync data to file succeed and inputBuffer size is:' + readLen);
        let imageSource = image.createImageSource(file.fd);
        let pixelMap = await imageSource.createPixelMap();
        let info = await pixelMap.getImageInfo();
        Logger.info('info.width = ' + info.size.width);
        Logger.info('info.height = ' + info.size.height);

        ...
    }
}

更多关于HarmonyOS鸿蒙Next中选择相册图片后,怎么获取图片的尺寸信息呢?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


使用 PhotoPickerComponent 返回的 BaseItemInfo(直接获取尺寸)

// 假设 item 是 PhotoPickerComponent 返回的 BaseItemInfo 对象
let width = item.width; // 单位:像素
let height = item.height;
console.info(`图片尺寸:宽 ${width}像素, 高 ${height}像素`);

当使用PhotoViewPicker获取datashare://格式URI时,需先通过PhotoAccessHelper获取媒体资源对象:

let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
let predicates = new dataSharePredicates.DataSharePredicates();
predicates.equalTo('uri', fileUri); // 使用返回的URI作为查询条件
let fetchOption: photoAccessHelper.FetchOptions = {
  fetchColumns: ['size'], // 明确查询尺寸字段
  predicates: predicates
};

let fetchResult = await phAccessHelper.getAssets(fetchOption);
let asset = await fetchResult.getFirstObject();
console.info('图片尺寸:', asset.get(photoAccessHelper.PhotoKeys.SIZE));

获取图片的Asset,可以读取更多的元数据。包括图片宽高、大小、修改时间、更多信息

async getUriAsset(uri: string): Promise<photoAccessHelper.PhotoAsset | null> {
  const predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
  predicates.equalTo(photoAccessHelper.PhotoKeys.URI, uri.toString());
  const fetchOptions: photoAccessHelper.FetchOptions = {
    fetchColumns: [
      photoAccessHelper.PhotoKeys.WIDTH,
      photoAccessHelper.PhotoKeys.HEIGHT,
      photoAccessHelper.PhotoKeys.SIZE
    ],
    predicates: predicates
  }

  try {
    const context = this.getUIContext().getHostContext();
    const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
    const fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
      await phAccessHelper.getAssets(fetchOptions);
    const photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
    fetchResult.close();
    return photoAsset
  } catch (err) {
    return null
  }
}

// 使用
 const asset = await this.getUriAsset(fileUri)
 if (asset) {
    const width = asset.get(photoAccessHelper.PhotoKeys.WIDTH) as number
    const height = asset.get(photoAccessHelper.PhotoKeys.HEIGHT) as number
 }

问题分析与解决方案

你遇到的问题是:使用 PhotoViewPicker 或 PhotoPickerComponent 返回的 URI 无法直接用于 image.createImageSource(uri),这会导致报错。

核心问题

PhotoViewPicker 返回的 URI 格式是 file://media/Photo/…,这是媒体文件 URI,不能直接传递给 createImageSource(uri),因为:

  1. createImageSource(uri) 期望的 uri 参数是沙箱路径,如 /data/storage/el2/base/files/test.jpg
  2. PhotoPicker 返回的是媒体库 URI,需要先转换为文件描述符(fd)

正确的解决方案(基于最新 API)

方案 1: 使用 fd 方式(推荐)

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';

async function getImageSizeFromPicker() {
  try {
    // 1. 使用 PhotoViewPicker 选择图片
    const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
    photoSelectOptions.maxSelectNumber = 1;
    const photoPicker = new photoAccessHelper.PhotoViewPicker();
    const photoSelectResult = await photoPicker.select(photoSelectOptions);
    const uri = photoSelectResult.photoUris[0];
    console.info('Selected photo uri: ' + uri);

    // 2. 通过 fileIo.openSync 打开 URI 获取文件描述符(fd)
    const file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);
    console.info('File fd: ' + file.fd);

    // 3. 使用 fd 创建 ImageSource
    const imageSource: image.ImageSource = image.createImageSource(file.fd);

    // 4. 获取图片信息(包含尺寸)
    const imageInfo: image.ImageInfo = await imageSource.getImageInfo();
    console.info('Image width: ' + imageInfo.size.width);
    console.info('Image height: ' + imageInfo.size.height);
    console.info('Image density: ' + imageInfo.density);
    console.info('Image mimeType: ' + imageInfo.mimeType);

    // 5. 释放资源
    await imageSource.release();
    fileIo.closeSync(file);

    return imageInfo.size; // 返回 { width: number, height: number }
  } catch (err) {
    const error = err as BusinessError;
    console.error(`Error: ${error.code}, ${error.message}`);
  }
}

方案 2: 使用 getImageInfoSync 同步方式(API 12+)

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { image } from '@kit.ImageKit';

function getImageSizeSync(uri: string): image.Size | undefined {
  try {
    // 1. 通过 URI 获取 fd
    const file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY);

    // 2. 创建 ImageSource
    const imageSource: image.ImageSource = image.createImageSource(file.fd);

    // 3. 同步获取图片信息
    const imageInfo: image.ImageInfo = imageSource.getImageInfoSync(0);
    console.info('Image width: ' + imageInfo.size.width);
    console.info('Image height: ' + imageInfo.size.height);

    // 4. 释放资源
    imageSource.release();
    fileIo.closeSync(file);

    return imageInfo.size;
  } catch (error) {
    console.error('Failed to get image size: ' + error);
    return undefined;
  }
}

方案 3: 使用 PhotoAccessHelper 获取详细信息

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { dataSharePredicates } from '@kit.ArkData';

async function getImageInfoFromUri(uri: string, phAccessHelper: photoAccessHelper.PhotoAccessHelper) {
  try {
    // 1. 通过 URI 查询 PhotoAsset
    let predicates = new dataSharePredicates.DataSharePredicates();
    predicates.equalTo(photoAccessHelper.PhotoKeys.URI, uri);
    let fetchOptions: photoAccessHelper.FetchOptions = {
      fetchColumns: [
        photoAccessHelper.PhotoKeys.WIDTH,
        photoAccessHelper.PhotoKeys.HEIGHT,
        photoAccessHelper.PhotoKeys.SIZE,
        photoAccessHelper.PhotoKeys.TITLE
      ],
      predicates: predicates
    };

    // 2. 获取资产
    const fetchResult = await phAccessHelper.getAssets(fetchOptions);
    const photoAsset = await fetchResult.getFirstObject();

    // 3. 获取图片宽高
    const width = photoAsset.get(photoAccessHelper.PhotoKeys.WIDTH);
    const height = photoAsset.get(photoAccessHelper.PhotoKeys.HEIGHT);
    const fileSize = photoAsset.get(photoAccessHelper.PhotoKeys.SIZE);
    const title = photoAsset.get(photoAccessHelper.PhotoKeys.TITLE);

    console.info(`Width: ${width}, Height: ${height}`);
    console.info(`File size: ${fileSize}, Title: ${title}`);

    fetchResult.close();
    return { width, height, fileSize, title };
  } catch (err) {
    console.error('Failed to get image info: ' + err);
  }
}

完整示例(组合方案)

import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { image } from '@kit.ImageKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct ImagePickerDemo {
  @State imageWidth: number = 0;
  @State imageHeight: number = 0;
  @State imageUri: string = '';

  async selectAndGetImageSize() {
    try {
      // 1. 选择图片
      const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
      photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
      photoSelectOptions.maxSelectNumber = 1;
      const photoPicker = new photoAccessHelper.PhotoViewPicker();
      const result = await photoPicker.select(photoSelectOptions);

      if (result.photoUris.length > 0) {
        this.imageUri = result.photoUris[0];

        // 2. 打开文件获取 fd
        const file = fileIo.openSync(this.imageUri, fileIo.OpenMode.READ_ONLY);

        // 3. 创建 ImageSource
        const imageSource = image.createImageSource(file.fd);

        // 4. 获取图片信息
        const imageInfo = await imageSource.getImageInfo();
        this.imageWidth = imageInfo.size.width;
        this.imageHeight = imageInfo.size.height;
        console.info(`Image size: ${this.imageWidth} x ${this.imageHeight}`);

        // 5. 清理资源
        await imageSource.release();
        fileIo.closeSync(file);
      }
    } catch (err) {
      const error = err as BusinessError;
      console.error(`Error: ${error.code}, ${error.message}`);
    }
  }

  build() {
    Column({ space: 20 }) {
      Button('选择图片并获取尺寸')
        .onClick(() => {
          this.selectAndGetImageSize();
        })

      if (this.imageUri) {
        Image(this.imageUri)
          .width(300)
          .height(300)
          .objectFit(ImageFit.Contain)

        Text(`宽度: ${this.imageWidth}px`)
        Text(`高度: ${this.imageHeight}px`)
      }
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

关键要点总结

  1. 不能直接使用 URI: PhotoPicker 返回的 URI 不能直接传给 createImageSource(uri)
  2. 必须使用 fd: 需要通过 fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY) 获取 fd
  3. 使用 fd 创建 ImageSource: image.createImageSource(file.fd)
  4. 获取图片信息: imageInfo.size.width 和 imageInfo.size.height
  5. 记得释放资源: 调用 imageSource.release() 和 fileIo.closeSync(file)

在HarmonyOS Next中,获取相册图片尺寸信息,可通过PhotoViewPicker选择图片后,使用image模块的getImageInfo方法。选择图片返回的URI,调用getImageInfo可异步获取包含size(width、height)的ImageInfo对象。

在HarmonyOS Next中,通过PhotoViewPickerPhotoPickerComponent获取到图片URI后,直接使用image.createImageSource(uri).getImageInfo()来获取尺寸信息(如widthheight)是正确的方法

从你提供的错误信息来看,问题很可能出在URI的格式或权限上。PhotoPickerComponent返回的URI可能是一个需要特殊处理的临时或虚拟路径,直接用于createImageSource可能导致失败。

解决方案:

  1. 使用PhotoViewPicker(推荐) 这是更稳定、官方推荐的方式。确保你已经正确声明了所需的权限(ohos.permission.READ_IMAGEVIDEO),并且在使用前用户已授权。获取URI的代码通常是正确的,getImageInfo()方法也应该能正常工作。如果无日志,请检查:

    • 权限是否已授予。
    • 代码是否被正确执行(可添加基础日志排查)。
    • getImageInfo()是一个异步操作,确保你正确处理了Promise或使用了async/await

    示例代码片段:

    import picker from '@ohos.file.picker';
    import image from '@ohos.multimedia.image';
    
    // 1. 创建并启动PhotoViewPicker(假设已获得权限)
    let photoPicker = new picker.PhotoViewPicker();
    // ... 配置photoPicker ...
    let uris = await photoPicker.select();
    
    if (uris && uris.length > 0) {
      let selectedUri = uris[0];
      // 2. 创建ImageSource并获取信息
      let imageSource = image.createImageSource(selectedUri);
      let imageInfo = await imageSource.getImageInfo();
      console.log(`图片尺寸: width=${imageInfo.size.width}, height=${imageInfo.size.height}`);
      // 使用完毕后释放资源
      imageSource.release();
    }
    
  2. 处理PhotoPickerComponent的URI PhotoPickerComponent返回的URI可能需要通过FilePickergetPhotoAccessHelper()getPhotoPicker()方法转换为可用的绝对路径,或者它本身是一个需要ohos.permission.READ_IMAGEVIDEO权限才能访问的URI。直接使用该URI调用createImageSource可能会因权限或路径格式问题而失败。

    • 检查权限:确保应用已申请并获得了ohos.permission.READ_IMAGEVIDEO权限。
    • 尝试转换URI:如果可能,查阅PhotoPickerComponent的API文档,看是否有方法将其返回的URI转换为标准的file://路径或通过ohos.file.fs API可访问的URI。

核心要点:

  • 权限是前提:无论使用哪种选择器,ohos.permission.READ_IMAGEVIDEO权限是读取媒体文件所必需的。
  • PhotoViewPicker更可靠:对于直接从相册选择图片并获取元信息的场景,PhotoViewPicker API是更直接和稳定的选择。
  • 异步操作getImageInfo()返回Promise,需使用await.then()获取结果。
  • 资源释放:使用完ImageSource后,调用其release()方法释放资源。

根据你描述的情况,建议优先采用PhotoViewPicker方案,并仔细检查权限管理代码。

回到顶部