鸿蒙NEXT:使用ArkTS实现数据库备份与导出功能
本文介绍了鸿蒙应用开发中数据库备份与导出的实现方法。首先分析了沙箱环境下数据库文件的存储路径,指出WAL临时文件可能导致导出不完整的问题。然后重点讲解了使用ArkTS实现数据库备份恢复的完整方案:通过database.close()关闭连接确保WAL文件合并,再执行备份操作;同时详细说明了存储权限申请、文件选择器使用等关键步骤。最后强调了路径兼容性和临时文件处理等注意事项,提供了一套完整的数据库导
·
在鸿蒙应用开发中,数据库的数据备份与导出是一个常见需求。本文将介绍如何使用ArkTS实现关系型数据库的备份、恢复及沙箱文件导出功能,解决数据库中临时文件(WAL)导致的数据导出不完整问题。
一、数据库在沙箱中的位置
鸿蒙应用的数据存储在应用沙箱中,沙箱路径结构如下:
/data/app/el2/100/base/com.example.interviewbaolongzhanshi/haps/entry/cache/图片名称
| 目录层级 | 路径结构 | 用途 |
| 应用沙箱根目录 | /data/storage/el[?] | 应用隔离存储空间 |
| 数据库目录 | /database/ | 数据库相关文件 |
| 具体数据库文件 | /entry/rdb/Mydata.db | 主数据库文件 |
| 临时文件 | /entry/rdb/Mydata.db-wal | WAL(预写式日志)文件 |
上面注意哦: el2是设备类型标识符(手机为e),应使用动态获取方式:
import app from '@ohos.app';
const context = app.getContext();
const dbDir = `${context.filesDir}/database/entry/rdb/`;
二、数据库备份的必要性
直接导出数据库文件可能面临两个问题:
- WAL文件未合并导致数据不完整
- 数据库连接未关闭可能导致导出失败
ArkTS提供了备份恢复API,可以解决这些问题:
class DatabaseService {
private database: relationalStore.RdbStore;
constructor() {
// 初始化数据库连接
const config = {name: 'Mydata.db'};
relationalStore.getRdbStore(context, config, (err, store) => {
this.database = store;
});
}
// 数据库备份
async backup(targetName: string) {
this.database.close(); // 关闭连接确保WAL文件合并
await this.database.backup(targetName);
this.database.open(); // 恢复连接
console.log('备份完成:', targetName);
}
// 数据库恢复
async restore(sourceName: string) {
this.database.close(); // 关闭连接
await this.database.restore(sourceName);
this.database.open(); // 重新打开
console.log('恢复完成:', sourceName);
}
}
三、完整导出实现方案
1. 权限申请
在导出前需要申请存储权限,在module.json5中添加:
"requestPermissions": [
{
"name": "ohos.permission.WRITE_USER_STORAGE"
}
]
运行时权限检查:
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
async requestStoragePermission() {
const atManager = abilityAccessCtrl.createAtManager();
try {
const { authResults } = await atManager.requestPermissionsFromUser(
context,
['ohos.permission.WRITE_USER_STORAGE']
);
if (authResults[0] === 0) {
console.log('存储权限已授予');
} else {
throw new Error('存储权限被拒绝');
}
} catch (err) {
console.error('权限申请失败:', err);
}
}
2. 数据库备份与导出
import picker from '@ohos.file.picker';
import fs from '@ohos.file.fs';
import { DatabaseService } from './DatabaseService'; // 数据库服务类
class DatabaseExporter {
private dbService = new DatabaseService();
// 导出数据库到用户目录
async exportDatabase() {
try {
// 申请权限
await this.requestStoragePermission();
// 生成备份文件名
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupName = `Backup_${timestamp}.db`;
// 执行备份
await this.dbService.backup(backupName);
// 获取备份文件路径
const backupPath = `${context.filesDir}/database/entry/rdb/${backupName}`;
// 使用文件选择器保存
const picker = new picker.DocumentViewPicker();
const saveOptions = {
newFileNames: [backupName],
types: ['*/*'] // 所有文件类型
};
const saveResult = await picker.saveAs(saveOptions);
const targetUri = saveResult[0];
// 执行文件拷贝
const targetFd = fs.openSync(targetUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
await fs.copyFile(backupPath, targetFd.fd);
fs.closeSync(targetFd);
// 删除临时备份文件
fs.unlink(backupPath);
console.log('数据库导出成功');
prompt.showToast({ message: '数据库导出成功' });
} catch (err) {
console.error('导出失败:', err);
prompt.showToast({ message: `导出失败: ${err.message}` });
}
}
}
3. 导入数据库文件
async importDatabase() {
try {
await this.requestStoragePermission();
// 选择文件
const picker = new picker.DocumentViewPicker();
const selectOptions = {
types: ['*.db'], // 只显示.db文件
multiple: false
};
const selectResult = await picker.select(selectOptions);
const sourceUri = selectResult[0];
// 验证文件有效性
const fileInfo = fs.statSync(sourceUri);
if (fileInfo.size < 1024) {
throw new Error('无效的数据库文件');
}
// 目标文件路径
const targetName = 'Restore.db';
const targetPath = `${context.filesDir}/database/entry/rdb/${targetName}`;
// 复制文件
const sourceFd = fs.openSync(sourceUri, fs.OpenMode.READ_ONLY);
await fs.copyFile(sourceFd.fd, targetPath);
fs.closeSync(sourceFd);
// 恢复数据库
await this.dbService.restore(targetName);
// 删除临时文件
fs.unlink(targetPath);
console.log('数据库导入成功');
prompt.showToast({ message: '数据库导入成功' });
} catch (err) {
console.error('导入失败:', err);
prompt.showToast({ message: `导入失败: ${err.message}` });
}
}
四、关键注意事项
-
路径兼容性
- 不同设备沙箱路径可能不同,使用
context.filesDir代替硬编码路径 - 文件操作前检查路径有效性:
fs.access(path). - 备份完成后删除临时文件
- 不同设备沙箱路径可能不同,使用
以上...后面根据个人需求优化用户/个人体验
创作来自 -- 宇智波斑-白面具
更多推荐




所有评论(0)