欢迎光临
我们一直在努力

鸿蒙中 文件选择器(FilePicker)

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

HarmonyOS的文件选择器(FilePicker)是一个系统级的组件,允许应用在不申请文件权限的情况下访问用户文件。通过拉起系统内置的文件管理界面,用户可以在图形化界面中选择文件,然后应用获得这些文件的临时访问权限。

特点

  • 无需权限:无需申请文件读写权限

  • 系统级组件:拉起系统文件管理界面,用户体验统一

  • 临时授权:获取的文件URI具有临时只读权限

  • 类型分类:支持文档、图片、音频等多种文件类型

主要选择器

选择器类型主要用途备注
PhotoViewPicker 图片和视频文件选择 已弃用,推荐使用PhotoAccessHelper的PhotoViewPicker
DocumentViewPicker 文档类文件选择 最常用的选择器,支持各种文件格式
AudioViewPicker 音频文件选择 用于选择音频文件

提示:通过Picker获得的URI权限是临时只读权限,应用退出后台后权限就会失效。如需持久化访问,需要进行持久化授权。

二、DocumentViewPicker:文档文件选择

2.1 使用示例

import { picker } from '@kit.CoreFileKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct FilePickerExample {
@State selectedFiles: string[] = []; // 存储选中的文件URI
@State fileInfo: string = '未选择文件';

build() {
Column({ space: 10 }) {
Text('文件选择器示例')
.fontSize(20)
.fontWeight(FontWeight.Bold)

Text(this.fileInfo)
.fontSize(16)
.margin({ bottom: 20 })

Button('选择文档文件')
.width('80%')
.height(50)
.onClick(() => {
this.selectDocumentFiles();
})

List({ space: 5 }) {
ForEach(this.selectedFiles, (uri: string, index: number) => {
ListItem() {
Text(`文件${index + 1}: ${this.getFileName(uri)}`)
.fontSize(14)
}
}, (uri: string) => uri)
}
.height('40%')
.width('100%')
}
.padding(15)
.width('100%')
.height('100%')
}

// 选择文档文件
private async selectDocumentFiles(): Promise<void> {
try {
// 1. 创建选择选项
const documentSelectOptions = new picker.DocumentSelectOptions();

// 2. 配置选项参数
documentSelectOptions.maxSelectNumber = 5; // 最多选择5个文件
documentSelectOptions.fileSuffixFilters = [
'图片(.png, .jpg)|.png,.jpg',
'文档|.txt',
'PDF文件|.pdf',
'视频|.mp4',
'所有文件(*.*)|.*' // API 17+ 支持通配符
];

// 3. 获取上下文
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;

// 4. 创建选择器实例
const documentViewPicker = new picker.DocumentViewPicker(context);

// 5. 调用select方法,拉起文件选择界面
const result = await documentViewPicker.select(documentSelectOptions);

// 6. 处理结果
this.selectedFiles = result;
this.fileInfo = `已选择 ${result.length} 个文件`;

console.info('文件选择成功,URI列表:', result);

} catch (error) {
const err = error as BusinessError;
console.error(`文件选择失败,错误码: ${err.code}, 消息: ${err.message}`);
this.fileInfo = `选择失败: ${err.message}`;
}
}

// 从URI中提取文件名
private getFileName(uri: string): string {
try {
const segments = uri.split('/');
return segments[segments.length – 1];
} catch {
return '未知文件';
}
}
}

2.2 DocumentSelectOptions 详细配置参数

const options = new picker.DocumentSelectOptions();

// 1. 基本配置
options.maxSelectNumber = 10; // 最多选择文件数(可选)
options.selectMode = picker.DocumentSelectMode.FILE; // 选择模式:FILE或DIR

// 2. 默认路径(可选)
options.defaultFilePathUri = "file://docs/storage/Users/currentUser/test";

// 3. 文件类型过滤(重要)
options.fileSuffixFilters = [
'图片文件|.png,.jpg,.jpeg,.gif,.bmp', // 显示名|后缀列表
'文档文件|.txt,.doc,.docx,.pdf',
'音频文件|.mp3,.wav,.flac,.aac',
'视频文件|.mp4,.avi,.mov,.mkv',
'压缩文件|.zip,.rar,.7z',
'所有文件(*.*)|.*' // 通配符,显示所有文件
];

// 4. 授权相关配置
options.authMode = false; // 是否授权模式(2in1设备有效)
options.multiAuthMode = false; // 批量授权模式(Phone设备有效)
options.multiUriArray = [ // 批量授权URI数组
"file://docs/storage/Users/currentUser/test",
"file://docs/storage/Users/currentUser/2test"
];

// 5. 视图模式
options.mergeMode = picker.MergeTypeMode.DEFAULT; // 聚合视图模式

// 6. 加密支持(API 19+)
options.isEncryptionSupported = false; // 是否支持文件加密

2.3 参数详细说明

maxSelectNumber
  • 作用:限制用户最多能选择的文件数量

  • 默认值:1

  • 范围:1-1000

fileSuffixFilters
  • 格式:'显示名称|后缀列表'

  • 示例:'图片|.png,.jpg'

  • 通配符:'.*' 表示所有文件(API 17+手机支持)

  • 注意事项:

    • 每个后缀名用英文逗号分隔

    • 后缀类型名不能超过100个字符

    • 支持多组过滤条件

selectMode
  • 取值:

    • picker.DocumentSelectMode.FILE:选择文件

    • picker.DocumentSelectMode.DIR:选择目录

  • 注意:此参数在2in1设备中效果明显,其他设备可能无区别

authMode 和 multiAuthMode
  • 适用设备:

    • authMode:主要针对2in1设备

    • multiAuthMode:主要针对Phone设备

  • 作用:控制是否拉起授权界面

三、读取选择的文件内容

3.1 基础文件读取

import { picker } from '@kit.CoreFileKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';

class FileReader {
private selectedFiles: string[] = [];

// 选择并读取文件
async selectAndReadFile(): Promise<void> {
try {
// 1. 选择文件
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const picker = new picker.DocumentViewPicker(context);

const options = new picker.DocumentSelectOptions();
options.maxSelectNumber = 1;

this.selectedFiles = await picker.select(options);

if (this.selectedFiles.length > 0) {
// 2. 读取第一个文件
await this.readFileContent(this.selectedFiles[0]);
}

} catch (error) {
console.error(`文件选择/读取失败: ${(error as BusinessError).message}`);
}
}

// 读取文件内容
private async readFileContent(uri: string): Promise<void> {
try {
// 1. 使用只读模式打开文件
const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
console.info('文件描述符:', file.fd);

// 2. 获取文件信息
const stat = fs.statSync(file.fd);
console.info('文件大小:', stat.size, '字节');
console.info('文件修改时间:', stat.mtime);

// 3. 读取文件内容
const bufferSize = Math.min(stat.size, 1024 * 1024); // 最多读取1MB
const buffer = new ArrayBuffer(bufferSize);

const bytesRead = fs.readSync(file.fd, buffer);
console.info(`实际读取 ${bytesRead} 字节`);

// 4. 处理读取的数据(示例:转换为文本)
if (this.isTextFile(uri)) {
const text = new TextDecoder().decode(new Uint8Array(buffer, 0, bytesRead));
console.info('文件内容预览:', text.substring(0, 100));
}

// 5. 关闭文件
fs.closeSync(file);

} catch (error) {
console.error(`文件读取失败: ${(error as BusinessError).message}`);
}
}

// 判断是否为文本文件(简单判断)
private isTextFile(uri: string): boolean {
const textExtensions = ['.txt', '.html', '.css', '.js', '.json', '.xml'];
return textExtensions.some(ext => uri.toLowerCase().endsWith(ext));
}

// 批量读取多个文件
async readMultipleFiles(): Promise<string[]> {
const contents: string[] = [];

for (const uri of this.selectedFiles) {
try {
const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
const stat = fs.statSync(file.fd);

// 仅读取小文件内容
if (stat.size < 1024 * 1024) { // 小于1MB
const buffer = new ArrayBuffer(stat.size);
fs.readSync(file.fd, buffer);

const text = new TextDecoder().decode(new Uint8Array(buffer));
contents.push(text);
} else {
contents.push(`[大文件: ${stat.size} 字节]`);
}

fs.closeSync(file);
} catch (error) {
contents.push(`[读取失败: ${uri}]`);
}
}

return contents;
}
}

四、AudioViewPicker:音频文件选择

4.1 音频文件选择基础

import { picker } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct AudioPickerExample {
@State selectedAudioFiles: string[] = [];
@State audioInfo: string = '未选择音频文件';

build() {
Column({ space: 10 }) {
Text('音频文件选择')
.fontSize(20)
.fontWeight(FontWeight.Bold)

Text(this.audioInfo)
.fontSize(16)

Button('选择音频文件')
.width('80%')
.height(50)
.onClick(() => {
this.selectAudioFiles();
})

// 音频文件列表
if (this.selectedAudioFiles.length > 0) {
List({ space: 5 }) {
ForEach(this.selectedAudioFiles, (uri: string, index: number) => {
ListItem() {
Column({ space: 5 }) {
Text(`音频 ${index + 1}`)
.fontSize(14)
.fontWeight(FontWeight.Bold)
Text(this.getAudioFileName(uri))
.fontSize(12)
.fontColor(Color.Gray)
}
.width('100%')
}
.onClick(() => {
this.playAudio(uri);
})
}, (uri: string) => uri)
}
.height('50%')
}
}
.padding(15)
}

// 选择音频文件
private async selectAudioFiles(): Promise<void> {
try {
// 1. 创建音频选择选项
const audioSelectOptions = new picker.AudioSelectOptions();
// 注意:AudioSelectOptions目前不支持参数配置

// 2. 获取上下文
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;

// 3. 创建音频选择器
const audioViewPicker = new picker.AudioViewPicker(context);

// 4. 调用select方法
const result = await audioViewPicker.select(audioSelectOptions);

// 5. 处理结果
this.selectedAudioFiles = result;
this.audioInfo = `已选择 ${result.length} 个音频文件`;

console.info('音频选择成功:', result);

} catch (error) {
const err = error as BusinessError;
console.error(`音频选择失败: ${err.message}`);
this.audioInfo = `选择失败: ${err.message}`;
}
}

// 播放音频(示例)
private playAudio(uri: string): void {
console.info('开始播放音频:', uri);
// 实际音频播放需要结合媒体服务API
// 参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/audio-playback-overview
}

private getAudioFileName(uri: string): string {
const segments = uri.split('/');
return segments[segments.length – 1];
}
}

4.2 音频文件处理

import { fileIo as fs } from '@kit.CoreFileKit';

class AudioFileProcessor {
// 读取音频文件信息
static async getAudioFileInfo(uri: string): Promise<AudioFileInfo> {
try {
const file = fs.openSync(uri, fs.OpenMode.READ_ONLY);

// 获取基本文件信息
const stat = fs.statSync(file.fd);

// 读取音频文件头信息(示例)
const headerBuffer = new ArrayBuffer(128);
const bytesRead = fs.readSync(file.fd, headerBuffer, { offset: 0 });

fs.closeSync(file);

return {
uri: uri,
size: stat.size,
duration: await this.estimateDuration(uri, stat.size),
format: this.detectAudioFormat(uri, new Uint8Array(headerBuffer)),
bitrate: await this.calculateBitrate(uri, stat.size),
sampleRate: 44100, // 需要实际解析音频文件
channels: 2 // 需要实际解析音频文件
};

} catch (error) {
console.error('获取音频文件信息失败:', error);
throw error;
}
}

// 简单的音频格式检测
private static detectAudioFormat(uri: string, header: Uint8Array): string {
const extension = uri.toLowerCase().split('.').pop() || '';

// 根据扩展名判断
if (['mp3', 'wav', 'flac', 'aac', 'ogg', 'm4a'].includes(extension)) {
return extension.toUpperCase();
}

// 可以根据文件头进一步判断
// 这里只是示例
return 'UNKNOWN';
}

// 估算音频时长(简单估算)
private static async estimateDuration(uri: string, fileSize: number): Promise<number> {
// 这是一个简化的估算,实际需要根据音频编码格式计算
// MP3估算:文件大小 / (比特率 / 8)
const estimatedBitrate = 128000; // 128kbps
return Math.round(fileSize * 8 / estimatedBitrate);
}

// 计算比特率
private static async calculateBitrate(uri: string, fileSize: number): Promise<number> {
try {
// 尝试从文件中读取时长信息
// 这里只是示例
const duration = await this.estimateDuration(uri, fileSize);
if (duration > 0) {
return Math.round(fileSize * 8 / duration);
}
return 0;
} catch {
return 0;
}
}
}

interface AudioFileInfo {
uri: string;
size: number;
duration: number; // 秒
format: string;
bitrate: number; // bps
sampleRate: number;
channels: number;
}

赞(0)
未经允许不得转载:171主机测评 » 鸿蒙中 文件选择器(FilePicker)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址