HarmonyOS鸿蒙Next中如何访问应用沙箱、选择系统文件和管理文件权限?
HarmonyOS鸿蒙Next中如何访问应用沙箱、选择系统文件和管理文件权限? 在HarmonyOS应用中如何进行文件读写操作?如何访问应用沙箱、选择系统文件和管理文件权限?
优秀
更多关于HarmonyOS鸿蒙Next中如何访问应用沙箱、选择系统文件和管理文件权限?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
解决方案
1. 基础文件读写
import fs from '@ohos.file.fs'
import { BusinessError } from '@ohos.base'
@Entry
@Component
struct BasicFileOperation {
private filesDir: string = getContext(this).filesDir
build() {
Column({ space: 16 }) {
Text('文件操作示例')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button('写入文本文件')
.width('100%')
.onClick(() => {
this.writeTextFile()
})
Button('读取文本文件')
.width('100%')
.onClick(() => {
this.readTextFile()
})
Button('追加内容')
.width('100%')
.onClick(() => {
this.appendToFile()
})
Button('删除文件')
.width('100%')
.onClick(() => {
this.deleteFile()
})
}
.padding(16)
}
private writeTextFile() {
try {
const filePath = `${this.filesDir}/test.txt`
const content = '这是测试内容\n当前时间: ' + new Date().toLocaleString()
// 写入文件
const file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE)
fs.writeSync(file.fd, content)
fs.closeSync(file.fd)
console.log('文件写入成功:', filePath)
} catch (error) {
const err = error as BusinessError
console.error('文件写入失败:', err.message)
}
}
private readTextFile() {
try {
const filePath = `${this.filesDir}/test.txt`
// 检查文件是否存在
if (!fs.accessSync(filePath)) {
console.log('文件不存在')
return
}
// 读取文件
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY)
const buffer = new ArrayBuffer(1024)
const readLen = fs.readSync(file.fd, buffer)
fs.closeSync(file.fd)
// 转换为字符串
const content = String.fromCharCode(...new Uint8Array(buffer.slice(0, readLen)))
console.log('文件内容:', content)
} catch (error) {
const err = error as BusinessError
console.error('文件读取失败:', err.message)
}
}
private appendToFile() {
try {
const filePath = `${this.filesDir}/test.txt`
const appendContent = '\n追加的内容'
const file = fs.openSync(filePath, fs.OpenMode.APPEND | fs.OpenMode.READ_WRITE)
fs.writeSync(file.fd, appendContent)
fs.closeSync(file.fd)
console.log('内容追加成功')
} catch (error) {
const err = error as BusinessError
console.error('追加失败:', err.message)
}
}
private deleteFile() {
try {
const filePath = `${this.filesDir}/test.txt`
fs.unlinkSync(filePath)
console.log('文件删除成功')
} catch (error) {
console.error('文件删除失败:', error)
}
}
}
2. 目录操作
import fs from '@ohos.file.fs'
@Entry
@Component
struct DirectoryOperation {
private filesDir: string = getContext(this).filesDir
build() {
Column({ space: 16 }) {
Text('目录操作')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button('创建目录')
.width('100%')
.onClick(() => {
this.createDirectory()
})
Button('列出目录内容')
.width('100%')
.onClick(() => {
this.listDirectory()
})
Button('删除目录')
.width('100%')
.onClick(() => {
this.deleteDirectory()
})
Button('复制文件')
.width('100%')
.onClick(() => {
this.copyFile()
})
}
.padding(16)
}
private createDirectory() {
try {
const dirPath = `${this.filesDir}/mydir`
// 创建目录
if (!fs.accessSync(dirPath)) {
fs.mkdirSync(dirPath)
console.log('目录创建成功:', dirPath)
} else {
console.log('目录已存在')
}
} catch (error) {
console.error('创建目录失败:', error)
}
}
private listDirectory() {
try {
// 列出目录中的所有文件和子目录
const files = fs.listFileSync(this.filesDir)
console.log('目录内容:')
files.forEach(file => {
console.log(' -', file)
})
} catch (error) {
console.error('列出目录失败:', error)
}
}
private deleteDirectory() {
try {
const dirPath = `${this.filesDir}/mydir`
if (fs.accessSync(dirPath)) {
fs.rmdirSync(dirPath)
console.log('目录删除成功')
}
} catch (error) {
console.error('删除目录失败:', error)
}
}
private copyFile() {
try {
const sourcePath = `${this.filesDir}/test.txt`
const destPath = `${this.filesDir}/test_copy.txt`
if (fs.accessSync(sourcePath)) {
fs.copyFileSync(sourcePath, destPath)
console.log('文件复制成功')
} else {
console.log('源文件不存在')
}
} catch (error) {
console.error('文件复制失败:', error)
}
}
}
3. 使用FilePicker选择文件
import picker from '@ohos.file.picker'
import fs from '@ohos.file.fs'
import { BusinessError } from '@ohos.base'
@Entry
@Component
struct FilePickerDemo {
@State selectedFileUri: string = ''
@State fileContent: string = ''
build() {
Column({ space: 16 }) {
Text('文件选择器')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button('选择文档')
.width('100%')
.onClick(() => {
this.selectDocument()
})
Button('选择图片')
.width('100%')
.onClick(() => {
this.selectImage()
})
Button('保存文件')
.width('100%')
.onClick(() => {
this.saveFile()
})
if (this.selectedFileUri) {
Text('已选文件:')
.fontSize(14)
.fontWeight(FontWeight.Bold)
Text(this.selectedFileUri)
.fontSize(12)
.fontColor('#666666')
}
if (this.fileContent) {
Text('文件内容:')
.fontSize(14)
.fontWeight(FontWeight.Bold)
Text(this.fileContent)
.fontSize(12)
.fontColor('#666666')
.maxLines(5)
}
}
.padding(16)
}
private async selectDocument() {
try {
const documentPicker = new picker.DocumentViewPicker()
const result = await documentPicker.select({
maxSelectNumber: 1,
defaultFilePathUri: '',
fileSuffixFilters: ['.txt', '.pdf', '.doc']
})
if (result && result.length > 0) {
this.selectedFileUri = result[0]
console.log('选择的文档:', this.selectedFileUri)
// 读取文件内容
await this.readSelectedFile(this.selectedFileUri)
}
} catch (error) {
const err = error as BusinessError
console.error('选择文档失败:', err.message)
}
}
private async selectImage() {
try {
const photoPicker = new picker.PhotoViewPicker()
const result = await photoPicker.select({
MIMEType: picker.PhotoViewMIMETypes.IMAGE_TYPE,
maxSelectNumber: 1
})
if (result && result.photoUris.length > 0) {
this.selectedFileUri = result.photoUris[0]
console.log('选择的图片:', this.selectedFileUri)
}
} catch (error) {
const err = error as BusinessError
console.error('选择图片失败:', err.message)
}
}
private async saveFile() {
try {
const documentPicker = new picker.DocumentViewPicker()
const result = await documentPicker.save({
newFileNames: ['output.txt'],
defaultFilePathUri: '',
fileSuffixChoices: ['.txt']
})
if (result && result.length > 0) {
const saveUri = result[0]
console.log('保存路径:', saveUri)
// 写入内容到选择的位置
const file = fs.openSync(saveUri, fs.OpenMode.WRITE_ONLY | fs.OpenMode.CREATE)
const content = '保存的内容\n时间: ' + new Date().toLocaleString()
fs.writeSync(file.fd, content)
fs.closeSync(file.fd)
console.log('文件保存成功')
}
} catch (error) {
const err = error as BusinessError
console.error('保存文件失败:', err.message)
}
}
private async readSelectedFile(uri: string) {
try {
const file = fs.openSync(uri, fs.OpenMode.READ_ONLY)
const buffer = new ArrayBuffer(2048)
const readLen = fs.readSync(file.fd, buffer)
fs.closeSync(file.fd)
this.fileContent = String.fromCharCode(...new Uint8Array(buffer.slice(0, readLen)))
} catch (error) {
console.error('读取文件失败:', error)
}
}
}
4. JSON文件读写
import fs from '@ohos.file.fs'
interface AppConfig {
theme: string
language: string
notifications: boolean
version: string
}
class ConfigManager {
private configPath: string
private defaultConfig: AppConfig = {
theme: 'light',
language: 'zh-CN',
notifications: true,
version: '1.0.0'
}
constructor() {
this.configPath = `${getContext().filesDir}/config.json`
}
/**
* 读取配置
*/
readConfig(): AppConfig {
try {
if (!fs.accessSync(this.configPath)) {
return this.defaultConfig
}
const file = fs.openSync(this.configPath, fs.OpenMode.READ_ONLY)
const buffer = new ArrayBuffer(4096)
const readLen = fs.readSync(file.fd, buffer)
fs.closeSync(file.fd)
const jsonStr = String.fromCharCode(...new Uint8Array(buffer.slice(0, readLen)))
return JSON.parse(jsonStr) as AppConfig
} catch (error) {
console.error('读取配置失败:', error)
return this.defaultConfig
}
}
/**
* 保存配置
*/
saveConfig(config: AppConfig): boolean {
try {
const jsonStr = JSON.stringify(config, null, 2)
const file = fs.openSync(
this.configPath,
fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY | fs.OpenMode.TRUNC
)
fs.writeSync(file.fd, jsonStr)
fs.closeSync(file.fd)
console.log('配置保存成功')
return true
} catch (error) {
console.error('保存配置失败:', error)
return false
}
}
/**
* 更新配置项
*/
updateConfig(key: keyof AppConfig, value: string | boolean): boolean {
const config = this.readConfig()
config[key] = value as never
return this.saveConfig(config)
}
}
// 使用示例
@Entry
@Component
struct ConfigDemo {
private configManager = new ConfigManager()
@State config: AppConfig = this.configManager.readConfig()
build() {
Column({ space: 16 }) {
Text('应用配置')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Row() {
Text('主题:')
.layoutWeight(1)
Text(this.config.theme)
.fontColor('#666666')
}
.width('100%')
Row() {
Text('语言:')
.layoutWeight(1)
Text(this.config.language)
.fontColor('#666666')
}
.width('100%')
Row() {
Text('通知:')
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.config.notifications })
.onChange((isOn) => {
this.config.notifications = isOn
this.configManager.saveConfig(this.config)
})
}
.width('100%')
Button('切换主题')
.width('100%')
.onClick(() => {
this.config.theme = this.config.theme === 'light' ? 'dark' : 'light'
this.configManager.saveConfig(this.config)
})
}
.padding(16)
}
}
5. 文件工具类
// utils/FileUtil.ets
import fs from '@ohos.file.fs'
import { BusinessError } from '@ohos.base'
export class FileUtil {
/**
* 读取文本文件
*/
static readText(filePath: string): string | null {
try {
if (!fs.accessSync(filePath)) {
return null
}
const file = fs.openSync(filePath, fs.OpenMode.READ_ONLY)
const stat = fs.statSync(filePath)
const buffer = new ArrayBuffer(stat.size)
fs.readSync(file.fd, buffer)
fs.closeSync(file.fd)
return String.fromCharCode(...new Uint8Array(buffer))
} catch (error) {
console.error('读取文件失败:', error)
return null
}
}
/**
* 写入文本文件
*/
static writeText(filePath: string, content: string): boolean {
try {
const file = fs.openSync(
filePath,
fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY | fs.OpenMode.TRUNC
)
fs.writeSync(file.fd, content)
fs.closeSync(file.fd)
return true
} catch (error) {
console.error('写入文件失败:', error)
return false
}
}
/**
* 读取JSON文件
*/
static readJson<T>(filePath: string): T | null {
const content = this.readText(filePath)
if (!content) {
return null
}
try {
return JSON.parse(content) as T
} catch (error) {
console.error('解析JSON失败:', error)
return null
}
}
/**
* 写入JSON文件
*/
static writeJson<T>(filePath: string, data: T): boolean {
try {
const jsonStr = JSON.stringify(data, null, 2)
return this.writeText(filePath, jsonStr)
} catch (error) {
console.error('序列化JSON失败:', error)
return false
}
}
/**
* 检查文件是否存在
*/
static exists(filePath: string): boolean {
try {
return fs.accessSync(filePath)
} catch {
return false
}
}
/**
* 获取文件大小(字节)
*/
static getSize(filePath: string): number {
try {
const stat = fs.statSync(filePath)
return stat.size
} catch {
return 0
}
}
/**
* 删除文件
*/
static delete(filePath: string): boolean {
try {
if (this.exists(filePath)) {
fs.unlinkSync(filePath)
return true
}
return false
} catch (error) {
console.error('删除文件失败:', error)
return false
}
}
/**
* 复制文件
*/
static copy(sourcePath: string, destPath: string): boolean {
try {
fs.copyFileSync(sourcePath, destPath)
return true
} catch (error) {
console.error('复制文件失败:', error)
return false
}
}
/**
* 移动文件
*/
static move(sourcePath: string, destPath: string): boolean {
try {
fs.moveFileSync(sourcePath, destPath)
return true
} catch (error) {
console.error('移动文件失败:', error)
return false
}
}
}
关键要点
- 沙箱目录: 应用只能访问自己的沙箱目录(filesDir、cacheDir等)
- 文件描述符: 使用openSync打开文件,操作完成后必须closeSync
- FilePicker: 访问系统文件需要使用选择器,避免直接路径访问
- 权限管理: 访问公共目录需要申请相应权限
- 异常处理: 所有文件操作都应该捕获异常
最佳实践
- 资源释放: 及时关闭文件描述符,避免资源泄漏
- 错误处理: 检查文件是否存在,捕获IO异常
- 性能优化: 大文件使用流式读写,避免一次性加载
- 缓存管理: 临时文件使用cacheDir,会被系统自动清理
- 数据安全: 敏感数据加密存储
在HarmonyOS Next中,应用沙箱路径可通过context.filesDir获取。选择系统文件使用PhotoViewPicker或DocumentViewPicker。文件权限需在module.json5中声明ohos.permission.READ_MEDIA或ohos.permission.WRITE_MEDIA,并在运行时通过requestPermissionsFromUser动态申请。
在HarmonyOS Next中,文件访问、沙箱管理和权限控制主要通过[@ohos](/user/ohos).file.fs(文件系统)、[@ohos](/user/ohos).file.picker(文件选择器)和[@ohos](/user/ohos).ability.accessCtrl(权限管理)等模块实现。以下是关键操作指南:
1. 应用沙箱访问
每个应用都有独立的沙箱目录,可通过context获取:
import UIAbility from '[@ohos](/user/ohos).app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
let context = this.context;
let filesDir = context.filesDir; // 应用文件存储路径
let cacheDir = context.cacheDir; // 缓存路径
}
}
沙箱路径无需权限即可直接读写。
2. 系统文件选择
使用[@ohos](/user/ohos).file.picker选择用户文件:
import picker from '[@ohos](/user/ohos).file.picker';
// 选择单个文件
let photoPicker = new picker.PhotoViewPicker();
photoPicker.select()
.then(photoSelectResult => {
let uri = photoSelectResult[0]; // 获取文件uri
})
.catch(err => console.error('选择失败'));
选择器返回文件URI,后续通过fs.open()等接口操作。
3. 文件读写操作
使用[@ohos](/user/ohos).file.fs进行文件操作:
import fs from '[@ohos](/user/ohos).file.fs';
// 写入沙箱文件
let filePath = context.filesDir + '/test.txt';
fs.writeText(filePath, 'Hello HarmonyOS')
.then(() => console.info('写入成功'))
.catch(err => console.error('写入失败'));
// 读取URI文件(如选择器返回的URI)
let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
let content = fs.readTextSync(file.fd);
fs.closeSync(file.fd);
4. 文件权限管理
- 沙箱文件:无需声明权限。
- 公共目录访问:需要声明对应权限并在动态权限弹窗中授权:
// module.json5 "requestPermissions": [ { "name": "ohos.permission.READ_MEDIA", "reason": "$string:reason_desc" // 需说明用途 } ]// 动态申请 import abilityAccessCtrl from '[@ohos](/user/ohos).ability.accessCtrl'; let atManager = abilityAccessCtrl.createAtManager(); atManager.requestPermissionsFromUser(this.context, ['ohos.permission.READ_MEDIA']) .then(result => { if (result.authResults[0] === 0) { // 授权成功 } });
关键注意事项
- 应用卸载时沙箱文件会自动清除,持久化数据需存至公共目录。
- 操作非沙箱文件必须通过
picker或声明权限,禁止直接路径访问。 - 文件URI权限具有临时性,建议立即操作或使用
persistentPermission接口申请持久化授权。
以上流程遵循HarmonyOS Next的安全设计,确保用户数据可控。

