
在 HarmonyOS 应用开发中,文件选择是高频场景需求,开发者经常需要实现:
如何基于 HarmonyOS API 20 提供的 FilePicker 能力,优雅实现跨目录、多格式的文件选择功能,并处理不同来源文件的权限与路径问题?这里我整理了一套完整的解决方案,希望可以帮助到大家。
我们先了解一下filepicker
HarmonyOS API 20 的 FilePicker 组件(文件选择器)是系统提供的统一文件选择接口,相比自定义文件浏览方案,具备以下核心优势:
应用沙箱目录(如 filesDir、cacheDir)下的文件为应用私有资源,选择时需指定沙箱路径,适用于读取应用生成或下载的文件。
import picker from '@ohos.file.picker';
import fs from '@ohos.file.fs';
import { BusinessError } from '@ohos.base';
@Entry
@Component
struct SandboxFilePickerDemo {
private context = getContext(this);
build() {
Column() {
Button('选择沙箱内PDF文件')
.width('80%')
.height(45)
.backgroundColor('#3498DB')
.fontColor(Color.White)
.borderRadius(25)
.margin({ top: 100 })
.onClick(() => this.pickSandboxFile());
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5');
}
// 选择沙箱内文件
private async pickSandboxFile() {
try {
// 1. 配置文件选择器参数
const filePickerOptions: picker.FilePickerOptions = {
// 指定沙箱目录路径
uri: fs.fileUri.getUriFromPath(this.context.filesDir),
// 筛选PDF格式文件
fileTypeFilter: ['application/pdf'],
// 支持多选
multiple: false
};
// 2. 创建文件选择器实例
const documentPicker = new picker.DocumentPicker();
// 3. 打开文件选择器
const result = await documentPicker.select(filePickerOptions);
if (result.files.length > 0) {
const selectedFile = result.files[0];
console.info(`选中文件:${selectedFile.name},路径:${selectedFile.uri}`);
// 4. 后续处理:如调用FilePreview预览(需导入filePreview模块)
this.previewFile(selectedFile.uri, selectedFile.name);
}
} catch (err) {
const error = err as BusinessError;
console.error(`沙箱文件选择失败:code=${error.code}, message=${error.message}`);
}
}
// 预览选中的文件
private previewFile(fileUri: string, fileName: string) {
import('@ohos.file.preview').then((filePreview) => {
const previewInfo: filePreview.PreviewInfo = {
title: fileName,
uri: fileUri,
mimeType: 'application/pdf'
};
filePreview.openPreview(this.context, previewInfo)
.then(() => console.info('文件预览成功'))
.catch((previewErr: BusinessError) => {
console.error(`预览失败:code=${previewErr.code}, message=${previewErr.message}`);
});
});
}
}公共目录(如图片、文档、下载目录)存储系统级共享文件,选择时需指定对应媒体类型,适用于读取用户导入的外部文件。
import picker from '@ohos.file.picker';
import { BusinessError } from '@ohos.base';
@Entry
@Component
struct PublicFilePickerDemo {
private context = getContext(this);
@State selectedFiles: Array<picker.SelectedFile> = [];
build() {
Column() {
Text('公共目录文件选择')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.margin({ top: 50, bottom: 30 });
// 图片选择按钮
Button('选择图片(PNG/JPG)')
.width('80%')
.height(45)
.backgroundColor('#E74C3C')
.fontColor(Color.White)
.borderRadius(25)
.margin({ bottom: 15 })
.onClick(() => this.pickPublicFile('image'));
// 文档选择按钮
Button('选择办公文档(Word/Excel/PDF)')
.width('80%')
.height(45)
.backgroundColor('#2ECC71')
.fontColor(Color.White)
.borderRadius(25)
.margin({ bottom: 15 })
.onClick(() => this.pickPublicFile('document'));
// 选中文件列表
List() {
ForEach(this.selectedFiles, (file) => {
ListItem() {
Text(file.name)
.fontSize(16)
.padding(15)
.width('100%')
.backgroundColor(Color.White)
.borderRadius(8)
.margin({ bottom: 10 });
}
})
}
.width('80%')
.height(200)
.margin({ top: 30 });
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.alignItems(HorizontalAlign.Center);
}
// 选择公共目录文件
private async pickPublicFile(fileType: 'image' | 'document' | 'video') {
try {
// 根据文件类型配置筛选条件
let filePickerOptions: picker.FilePickerOptions = {};
switch (fileType) {
case 'image':
filePickerOptions = {
uri: 'file:///storage/emulated/0/Pictures',
fileTypeFilter: ['image/png', 'image/jpeg'],
multiple: true
};
break;
case 'document':
filePickerOptions = {
uri: 'file:///storage/emulated/0/Documents',
fileTypeFilter: [
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
],
multiple: true
};
break;
case 'video':
filePickerOptions = {
uri: 'file:///storage/emulated/0/Movies',
fileTypeFilter: ['video/mp4', 'video/mkv'],
multiple: false
};
break;
}
// 创建文件选择器并打开
const documentPicker = new picker.DocumentPicker();
const result = await documentPicker.select(filePickerOptions);
if (result.files.length > 0) {
this.selectedFiles = result.files;
console.info(`成功选择${result.files.length}个文件`);
}
} catch (err) {
const error = err as BusinessError;
console.error(`公共文件选择失败:code=${error.code}, message=${error.message}`);
}
}
}文件选择后常需上传至服务器,以下示例实现选择文件后通过 HTTP 请求上传文件流。
import picker from '@ohos.file.picker';
import fs from '@ohos.file.fs';
import request from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
@Entry
@Component
struct FileUploadDemo {
private context = getContext(this);
@State uploadStatus: string = '未选择文件';
build() {
Column() {
Text('文件选择与上传')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.margin({ top: 50, bottom: 30 });
Button('选择并上传文件')
.width('80%')
.height(45)
.backgroundColor('#9B59B6')
.fontColor(Color.White)
.borderRadius(25)
.onClick(() => this.pickAndUploadFile());
Text(this.uploadStatus)
.fontSize(16)
.margin({ top: 30 })
.fontColor(this.uploadStatus.includes('成功') ? '#2ECC71' : '#E74C3C');
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
.alignItems(HorizontalAlign.Center);
}
// 选择文件并上传
private async pickAndUploadFile() {
try {
// 1. 选择文件
const filePickerOptions: picker.FilePickerOptions = {
uri: 'file:///storage/emulated/0/Documents',
fileTypeFilter: ['application/pdf', 'image/png'],
multiple: false
};
const documentPicker = new picker.DocumentPicker();
const result = await documentPicker.select(filePickerOptions);
if (result.files.length === 0) {
this.uploadStatus = '未选择任何文件';
return;
}
const selectedFile = result.files[0];
this.uploadStatus = `正在上传:${selectedFile.name}`;
// 2. 读取文件流
const file = await fs.open(selectedFile.uri, fs.OpenMode.READ_ONLY);
const fileStream = fs.createStream(file.fd, {
readStart: 0,
readEnd: file.size - 1
});
// 3. 上传文件到服务器
await this.uploadFileToServer(selectedFile.name, fileStream);
// 4. 关闭文件流
await fs.close(file);
} catch (err) {
const error = err as BusinessError;
this.uploadStatus = `上传失败:${error.message}`;
console.error(`文件处理失败:code=${error.code}, message=${error.message}`);
}
}
// 上传文件到服务器
private async uploadFileToServer(fileName: string, fileStream: fs.Stream) {
// 创建HTTP请求
const httpClient = request.createHttp();
const uploadUrl = 'https://your-server-url/upload'; // 替换为实际上传接口
try {
// 配置请求参数
const requestOptions: request.UploadRequestOptions = {
url: uploadUrl,
files: [
{
filename: fileName,
name: 'file',
stream: fileStream
}
],
header: {
'Content-Type': 'multipart/form-data'
}
};
// 发送上传请求
const response = await httpClient.upload(requestOptions);
if (response.responseCode === 200) {
this.uploadStatus = `上传成功:${fileName}`;
console.info('文件上传成功,响应:', response.result.toString());
} else {
this.uploadStatus = `上传失败:状态码${response.responseCode}`;
console.error('文件上传失败,响应码:', response.responseCode);
}
} catch (err) {
const error = err as BusinessError;
this.uploadStatus = `上传异常:${error.message}`;
console.error(`上传请求失败:code=${error.code}, message=${error.message}`);
} finally {
// 关闭HTTP客户端
httpClient.destroy();
}
}
}1.权限配置:访问公共目录文件需在module.json5中配置对应权限:
"requestPermissions": [
{
"name": "ohos.permission.READ_MEDIA_IMAGES",
"reason": "需要读取图片文件",
"usedScene": {
"abilities": ["MainAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.READ_MEDIA_DOCUMENTS",
"reason": "需要读取文档文件",
"usedScene": {
"abilities": ["MainAbility"],
"when": "always"
}
}
]2.格式筛选:fileTypeFilter需使用标准 MIME 类型,常见类型对应关系:
PDF:application/pdf
Word:application/msword(doc)、application/vnd.openxmlformats-officedocument.wordprocessingml.document(docx)
Excel:application/vnd.ms-excel(xls)、application/vnd.openxmlformats-officedocument.spreadsheetml.sheet(xlsx)
图片:image/png、image/jpeg、image/gif
视频:video/mp4、video/mkv
3.错误处理:需处理文件不存在、权限不足、网络异常等常见错误,通过BusinessError获取错误码进行针对性处理。
4.性能优化:选择大文件时建议使用流式读取,避免一次性加载整个文件到内存导致 OOM;多文件选择时限制最大选择数量。
HarmonyOS API 20 的 FilePicker 组件提供了统一的文件选择解决方案,支持沙箱目录与公共目录的文件访问,结合 FilePreview 可实现文件预览,结合网络请求可实现文件上传。开发时需根据文件来源(沙箱 / 公共目录)选择合适的配置参数,做好权限申请与错误处理,确保功能的稳定性与用户体验。当然也要提醒一下大家,不同版本的 HarmonyOS 可能存在 API 差异,实际开发中需结合官方文档与设备系统版本进行适配调整。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。