Harmony OS:用户文件操作——图片选择与渲染使用

本文将通过在HarmonyOS系统上获取图片、渲染图片、读取图片信息和保存图片的案例说明如何在HarmonyOS下对用户文件进行操作。

用户文件概述

核心概念:

  • 用户文件: 指用户在设备上创建或保存的私有数据,如图片、视频、文档等。

  • 访问权限: 应用访问用户文件需要用户授权或由用户主动操作。

  • 存储位置: 分为内置存储和外置存储。

存储位置详解:

  1. 内置存储 (不可移除):

    • 用户特有文件: 每个用户登录后只能看到自己的文件,根据文件类型分为:

      • 图片/视频类媒体文件: 以媒体文件形式存储(有元数据如拍摄时间地点等),通常以相册/所有文件形式展示,不显示具体存储路径。

      • 音频类媒体文件: 以媒体文件形式存储(有元数据如专辑、作者等),通常以专辑/所有文件等形式展示,不显示具体存储路径。

      • 其他文件(文档类文件): 以普通文件形式存储,包括文本、压缩文件以及以文件形式存储的媒体文件,以目录树形式展示。

    • 多用户共享文件: 用户可以将文件放置于共享区域,允许其他用户访问,以目录树形式展示。

  2. 外置存储 (可插拔,如SD卡、U盘):

    • 所有用户可见: 所有登录用户都可以看到外置存储上的文件。

    • 文件呈现形式: 所有文件都以普通文件形式(目录树)展示,与内置存储的"文档类文件"相同。

    • 设备管理: 系统提供设备插拔事件监听和挂载功能(仅对系统应用开放)来管理外置存储。

用户文件uri介绍

用户文件uri是文件的唯一标识,在对用户文件进行访问与修改等操作时往往都会使用到uri。

uri类型可以归纳为文档类uri媒体文件uri两类:

**文档类uri:**由picker拉起文件管理器选择或保存返回,以及通过fileAccess模块获取。

TypeScript 复制代码
// 导入相关依赖
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import  { picker } from '@kit.CoreFileKit';

// 获取UIAbility上下文
private context = getContext(this) as common.UIAbilityContext;
@State files: string[] = []
 // 需确保 context 由 UIAbilityContext 转换而来
async function example08(context: common.Context) {
  try {
    // 设置文件选择器的基本配置
    let documentSelectOptions = new picker.DocumentSelectOptions();
    // 拉起文件选择器
    let documentPicker = new picker.DocumentViewPicker(context);
    documentPicker.select(documentSelectOptions, (err: BusinessError, documentSelectResult: Array<string>) => {
      if (err) {
        // 获取文件选择结果(文件的uri)并赋值给变量
        this.files = documentSelectResult
        console.error('DocumentViewPicker.select failed with err: ' + JSON.stringify(err));
        return;
      }
      console.info('DocumentViewPicker.select successfully, documentSelectResult uri: ' + JSON.stringify(documentSelectResult));
    });
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error('DocumentViewPicker failed with err: ' + JSON.stringify(err));
  }
}

注意: normal等级的应用使用此类uri的方式只能通过fs模块进行进一步处理,其他模块使用此uri是会报没有权限的错误。

**媒体文件uri:**由picker通过拉起图库选择图片或者视频返回,通过photoAccessHelper模块获取图片或者视频文件的uri,以及通过userFileManager模块获取图片、视频或者音频文件的uri。

TypeScript 复制代码
import { BusinessError } from '@kit.BasicServicesKit';

@State imgs: string[] = []

async function example01() {
  try {
    // 设置图像选择器的基本配置
    let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    // 设置待选择的图像类型为Image图片,除此之外还有VIDEO_TYPE视频、IMAGE_VIDEO_TYPE图片视频、MOVING_PHOTO_IMAGE_TYPE动态照片
    PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
    // 设置最大可选择图片数量为5
    PhotoSelectOptions.maxSelectNumber = 5;
    // 拉起图像选择器
    let photoPicker = new photoAccessHelper.PhotoViewPicker();
    photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {
      // 将选择图片的uri数组赋值给变量
      this.imgs = PhotoSelectResult..photoUris
      console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult));
    }).catch((err: BusinessError) => {
      console.error(`PhotoViewPicker.select failed with err: ${err.code}, ${err.message}`);
    });
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`PhotoViewPicker failed with err: ${err.code}, ${err.message}`);
  }
}

媒体文件uri介绍

媒体文件uri的格式类型为:

图片uri格式:

  • 'file://media/Photo/<id>/IMG_datetime_0001/displayName.jpg'

视频uri格式:

  • 'file://media/Photo/<id>/VID_datetime_0001/displayName.mp4'

音频uri格式:

  • 'file://media/Audio/<id>/AUD_datetime_0001/displayName.mp3'
uri字段 说明
'file://media' 表示这个uri是媒体文件。
'Photo' Photo表示这个uri是媒体文件中的图片或者视频类文件。
'Audio' 表示这个uri是媒体文件中的音频类文件。
'<id>' 表示在数据库中多个表中处理后的值,并不是指表中的file_id列,注意请不要使用此id去数据库中查询具体文件。
'IMG_datetime_0001' 表示图片文件在用户文件系统中存储的文件名去掉后缀剩下的部分。
'VID_datetime_0001' 表示视频文件在用户文件系统中存储的文件名去掉后缀剩下的部分。
'AUD_datetime_0001' 表示音频文件在用户文件系统中存储的文件名去掉后缀剩下的部分。

媒体文件uri的使用方式

normal等级的应用使用此类uri可以通过photoAccessHelper模块进行进一步处理。此接口需要申请相册管理模块读权限'ohos.permission.READ_IMAGEVIDEO',在使用中需要注意应用是否有此权限。

申请权限参考文档如下:

文档中心https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/declare-permissions-in-acl-V5申请受限权限流程非常复杂。建议通过临时授权的方式使用PhotoAccessHelper的PhotoViewPicker得到的uri使用photoAccessHelper.getAssets接口获取对应uri的PhotoAsset对象。

这种方式获取的对象可以调用getThumbnail获取缩略图和使用get接口读取PhotoKeys中的部分信息。

TypeScript 复制代码
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { dataSharePredicates } from '@kit.ArkData';

// 定义一个uri数组,用于接收PhotoViewPicker选择图片返回的uri
let uris: Array<string> = [];
const context = getContext(this);

// 调用PhotoViewPicker.select选择图片,此部分逻辑与上面保持一致
async function photoPickerGetUri() {
  try {  
    let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
    PhotoSelectOptions.maxSelectNumber = 1;
    let photoPicker = new photoAccessHelper.PhotoViewPicker();
    photoPicker.select(PhotoSelectOptions).then((PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {
      console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(PhotoSelectResult));
      uris = PhotoSelectResult.photoUris;
    }).catch((err: BusinessError) => {
      console.error('PhotoViewPicker.select failed with err: ' + JSON.stringify(err));
    });
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error('PhotoViewPicker failed with err: ' + JSON.stringify(err));
  }
}
// 通过photoAccessHelper临时访问文件
async function uriGetAssets() {
try {
    // 创建photoAccessHelper类实例
    let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
    // 设置查询结果的筛选条件
    let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
    // 配置查询条件,使用PhotoViewPicker选择图片返回的uri进行查询
    predicates.equalTo('uri', uris[0]);
    // fetchColumns用于筛选查询信息
    let fetchOption: photoAccessHelper.FetchOptions = {
      fetchColumns: [photoAccessHelper.PhotoKeys.WIDTH, photoAccessHelper.PhotoKeys.HEIGHT, photoAccessHelper.PhotoKeys.TITLE, photoAccessHelper.PhotoKeys.DURATION],
      predicates: predicates
    };
    let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOption);
    // 得到uri对应的PhotoAsset对象,读取文件的部分信息
    const asset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
    console.info('asset displayName: ', asset.displayName);
    console.info('asset uri: ', asset.uri);
    console.info('asset photoType: ', asset.photoType);
    console.info('asset width: ', asset.get(photoAccessHelper.PhotoKeys.WIDTH));
    console.info('asset height: ', asset.get(photoAccessHelper.PhotoKeys.HEIGHT));
    console.info('asset title: ' + asset.get(photoAccessHelper.PhotoKeys.TITLE));
    // 获取缩略图的PixelMap,PixelMap可以用于Image组件渲染,同时Image组件也可以直接使用uri渲染
    asset.getThumbnail((err, pixelMap) => {
      if (err == undefined) {
        console.info('getThumbnail successful ' + JSON.stringify(pixelMap));
      } else {
        console.error('getThumbnail fail', err);
      }
    });
  } catch (error){
    console.error('uriGetAssets failed with err: ' + JSON.stringify(error));
  }
}

同时,要获取图片进行网络传输或者进行分析,可以通过photoAccessHelper获取图片或者视频文件的ArrayBuffer进行传输:

TypeScript 复制代码
import { dataSharePredicates } from '@kit.ArkData';
// 获取上下文信息
const context = getContext(this);
// 根据上下文信息创建photoAccessHelper实例
let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);

// 自定义类,用于图片或视频的ArrayBuffer处理
class MediaDataHandler implements photoAccessHelper.MediaAssetDataHandler<ArrayBuffer> {
  onDataPrepared(data: ArrayBuffer) {
    if (data === undefined) {
      console.error('Error occurred when preparing data');
      return;
    }
    console.info('on image data prepared');
    // 应用自定义对资源数据的处理逻辑
  }
}

async function example() {
  // 创建文件筛选条件
  let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
  // 文件uri,可以从photoAccessHelper获取
  let uri = 'file://media/Photo/1/IMG_datetime_0001/displayName.jpg' // 需保证此uri已存在。
  // 设置筛选条件
  predicates.equalTo(photoAccessHelper.PhotoKeys.URI, uri.toString());
  let fetchOptions: photoAccessHelper.FetchOptions = {
    // 获取图片的名称(TITLE)
    fetchColumns: [photoAccessHelper.PhotoKeys.TITLE],
    // 按条件对查询结果进行筛选
    predicates: predicates
  };

  try {
    let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOptions);
    // 根据photoAccessHelper获取资源文件实例
    let photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
    console.info('getAssets photoAsset.uri : ' + photoAsset.uri);
    // 获取属性值,以标题为例;对于非默认查询的属性,get前需要在fetchColumns中添加对应列名
    console.info('title : ' + photoAsset.get(photoAccessHelper.PhotoKeys.TITLE));
    // 请求图片资源数据
    let requestOptions: photoAccessHelper.RequestOptions = {
      deliveryMode: photoAccessHelper.DeliveryMode.HIGH_QUALITY_MODE,
    }
    // 获取图片资源数据,随后根据自定义类中的逻辑对图片资源的ArrayBuffer进行处理。
    await photoAccessHelper.MediaAssetManager.requestImageData(context, photoAsset, requestOptions, new MediaDataHandler());
    console.info('requestImageData successfully');
    fetchResult.close();
  } catch (err) {
    console.error('getAssets failed with err: ' + err);
  }
}

如果从网络下载资源同时保存到沙箱后,可以保存至媒体资源库:

TypeScript 复制代码
import { photoAccessHelper } from '@kit.MediaLibraryKit';

@Entry
@Component
struct Index {
    saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  } // 设置安全控件按钮属性

  build() {
    Row() {
      Column() {
        SaveButton(this.saveButtonOptions) // 创建安全控件按钮
          .onClick(async (event, result: SaveButtonOnClickResult) => {
             if (result == SaveButtonOnClickResult.SUCCESS) {
               try {
                 let context = getContext();
                 let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
                 // 需要确保fileUri对应的资源存在
                 // 媒体资源需要存放在沙箱中,所以需要提前将网络资源加载到缓存cacheDir或者保存到永久文件路径filesDir中
                 let fileUri = 'file://com.example.temptest/data/storage/el2/base/haps/entry/files/test.jpg';
                 let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(context, fileUri);
                 await phAccessHelper.applyChanges(assetChangeRequest);
                 console.info('createAsset successfully, uri: ' + assetChangeRequest.getAsset().uri);
               } catch (err) {
                 console.error(`create asset failed with error: ${err.code}, ${err.message}`);
               }
             } else {
               console.error('SaveButtonOnClickResult create asset failed');
             }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

FetchOptions中fetchColumns中可以传入的PhotoKeys中支持临时授权方式可以读取的信息:

名称 说明
URI 'uri' 文件uri。
PHOTO_TYPE 'media_type' 媒体文件类型。
DISPLAY_NAME 'display_name' 显示名字。
SIZE 'size' 文件大小。
DATE_ADDED 'date_added' 文件创建时的Unix时间戳(单位:秒)。
DATE_MODIFIED 'date_modified' 文件修改时的Unix时间戳(单位:秒)。修改文件名不会改变此值,当文件内容发生修改时才会更新。
DURATION 'duration' 持续时间(单位:毫秒)。
WIDTH 'width' 图片宽度(单位:像素)。
HEIGHT 'height' 图片高度(单位:像素)。
DATE_TAKEN 'date_taken' 拍摄时的Unix时间戳(单位:秒)。
ORIENTATION 'orientation' 图片文件的方向。
TITLE 'title' 文件标题。

ArrayBuffer

ArrayBuffer 对象是:

  • 原始二进制数据缓冲区: ArrayBuffer 对象用于表示一段通用的、固定长度的原始二进制数据缓冲区 (raw binary data buffer)。它可以看作是内存中的一段区域,用来存储字节数据。

  • 不能直接操作: 不能直接读取或写入 ArrayBuffer 的内容。

  • 构造函数: ArrayBuffer() 构造函数创建一个以字节为单位的给定长度的新 ArrayBuffer

  • 可转移对象: ArrayBuffer 是一个可转移对象,可以在不同的执行上下文之间传输

ArrayBuffer对象可以用于创建ImageSource实例,随后通过ImageSource实例实现ArrayBuffer至PixelMap之间的转换。

TypeScript 复制代码
let ops: image.SourceOptions = {
    sourceDensity: 98,
  }
let imageSource: image.ImageSource = image.createImageSource(rawFile.buffer as ArrayBuffer, ops);
let commodityPixelMap: image.PixelMap = await imageSource.createPixelMap();
let pictureObj: image.Picture = image.createPicture(commodityPixelMap);
相关推荐
OreoCC15 分钟前
第N5周:Pytorch文本分类入门
人工智能·pytorch·python
紫雾凌寒2 小时前
计算机视觉应用|自动驾驶的感知革命:多传感器融合架构的技术演进与落地实践
人工智能·机器学习·计算机视觉·架构·自动驾驶·多传感器融合·waymo
sauTCc2 小时前
DataWhale-三月学习任务-大语言模型初探(一、二、五章学习)
人工智能·学习·语言模型
暴力袋鼠哥3 小时前
基于深度学习的中文文本情感分析系统
人工智能·深度学习
视觉语言导航3 小时前
RAG助力机器人场景理解与具身操作!EmbodiedRAG:基于动态三维场景图检索的机器人任务规划
人工智能·深度学习·具身智能
岱宗夫up3 小时前
《加快应急机器人发展的指导意见》中智能化升级的思考——传统应急设备智能化升级路径与落地实践
人工智能·aigc
訾博ZiBo3 小时前
AI日报 - 2025年3月12日
人工智能
龚大龙3 小时前
机器学习(李宏毅)——Auto-Encoder
人工智能·机器学习
snow@li4 小时前
AI问答:transformer 架构 / 模型 / 自注意力机制实现序列数据的并行处理 / AI的底层
人工智能·深度学习·transformer
cv2016_DL4 小时前
siglip2推理教程
人工智能·transformer