本文将通过在HarmonyOS系统上获取图片、渲染图片、读取图片信息和保存图片的案例说明如何在HarmonyOS下对用户文件进行操作。
用户文件概述
核心概念:
-
用户文件: 指用户在设备上创建或保存的私有数据,如图片、视频、文档等。
-
访问权限: 应用访问用户文件需要用户授权或由用户主动操作。
-
存储位置: 分为内置存储和外置存储。
存储位置详解:
-
内置存储 (不可移除):
-
用户特有文件: 每个用户登录后只能看到自己的文件,根据文件类型分为:
-
图片/视频类媒体文件: 以媒体文件形式存储(有元数据如拍摄时间地点等),通常以相册/所有文件形式展示,不显示具体存储路径。
-
音频类媒体文件: 以媒体文件形式存储(有元数据如专辑、作者等),通常以专辑/所有文件等形式展示,不显示具体存储路径。
-
其他文件(文档类文件): 以普通文件形式存储,包括文本、压缩文件以及以文件形式存储的媒体文件,以目录树形式展示。
-
-
多用户共享文件: 用户可以将文件放置于共享区域,允许其他用户访问,以目录树形式展示。
-
-
外置存储 (可插拔,如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);