HarmonyOS鸿蒙Next中Download/包名/目录下的db怎么才能访问
HarmonyOS鸿蒙Next中Download/包名/目录下的db怎么才能访问 现在我们在在公共目录下创建Download/包名/目录 中放了.db数据库,通过
const STORE_CONFIG: relationalStore.StoreConfig = {
name: dbName,
securityLevel: relationalStore.SecurityLevel.S1,
isReadOnly: this.isReadOnly(),
tokenizer: tokenType,
rootDir: this.getAppDbPath()//绝对路径
}
const context = AppUtil.getContext();
const rdbStore = await relationalStore.getRdbStore(context, STORE_CONFIG)
其中this.getAppDbPath()//绝对路径:/storage/Users/currentUser/Download/com.xxx/app.db
不管是customDir还是rootDir
也不管是写成父路径/storage/Users/currentUser/Download/com.xxx/ 还是绝对路径
都无法访问到。
有什么其他方法能对该目录下的数据库进行读写操作?
因为Download/com.xxx/ 会放很多文件夹 每个文件夹下都有相同名称的 .db数据库,不会放到沙箱中
更多关于HarmonyOS鸿蒙Next中Download/包名/目录下的db怎么才能访问的实战教程也可以访问 https://www.itying.com/category-93-b0.html
relationalStore.StoreConfig只要配置rootDir,无论什么可访问路径,都是只读模式。
- 如果只读,可以借助DocumentViewPicker,pickMode为DOWNLOAD获取Download目录权限打开db。
- 如果要读写,就得复制到databaseDir/rdb目录下,并且relationalStore.StoreConfig不能配置rootDir。
完整示例:
import { common } from '@kit.AbilityKit';
import { fileIo, fileUri, picker } from '@kit.CoreFileKit';
import { relationalStore } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import { resourceManager } from '@kit.LocalizationKit';
@Entry
@Component
struct Index {
private commonContext: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
private promptAction = this.getUIContext().getPromptAction();
private rdbStore: relationalStore.RdbStore | undefined = undefined;
build() {
Column({ space: 20 }) {
Button('打开Download DB(只读)')
.type(ButtonType.ROUNDED_RECTANGLE)
.onClick(() => {
this.getDownloadRdb(false,'test.db');
});
Button('复制打开Download DB(读写)')
.type(ButtonType.ROUNDED_RECTANGLE)
.onClick(() => {
this.getDownloadRdb(true,'test.db');
});
Button('创建表')
.type(ButtonType.ROUNDED_RECTANGLE)
.onClick(() => {
this.createTable();
});
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%');
}
getDownloadRdb(write:boolean, dbName:string){
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const documentViewPicker = new picker.DocumentViewPicker(context);
documentViewPicker.save({ pickerMode:picker.DocumentPickerMode.DOWNLOAD }).then((documentSaveResult: string[]) => {
const uri = new fileUri.FileUri(documentSaveResult[0]);
if (write) {
let srcFile = `${uri.path}/${dbName}`;
let destFile = `${this.commonContext.databaseDir}/rdb/${dbName}`;//db放在这个文件夹
let copied = this.copyFile(srcFile, destFile);
if (copied) {
let storeConfig: relationalStore.StoreConfig = {
name: dbName,
securityLevel: relationalStore.SecurityLevel.S1,
};
this.initDataBase(storeConfig);
}
}else{
let storeConfig: relationalStore.StoreConfig = {
name: dbName,
securityLevel: relationalStore.SecurityLevel.S1,
rootDir:uri.path,//配置此项, 只读
};
this.initDataBase(storeConfig);
}
})
}
copyFile(srcFilePath: string, targetFilePath: string): boolean {
if (!fileIo.accessSync(srcFilePath)) {
this.getUIContext().getPromptAction().showToast({ message: '源文件不存在' });
return false;
}
let srcFile = fileIo.openSync(srcFilePath);
let destFile = fileIo.openSync(targetFilePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
fileIo.copyFile(srcFile.fd, destFile.fd).then(() => {
this.getUIContext().getPromptAction().showToast({ message: '成功复制' });
}).catch((error: BusinessError) => {
this.getUIContext().getPromptAction().showToast({ message: '复制异常' + error.message });
return false;
}).finally(() => {
fileIo.closeSync(srcFile.fd);
fileIo.closeSync(destFile.fd);
})
return true;
}
initDataBase(config:relationalStore.StoreConfig) {
try {
relationalStore.getRdbStore(this.commonContext, config)
.then(async (rdbStore: relationalStore.RdbStore) => {
this.rdbStore = rdbStore;
console.info('Get RdbStore successfully.');
this.promptAction.showToast({ message: `数据库打开[或创建打开]成功` });
})
.catch((err: BusinessError) => {
console.error(`Get RdbStore failed, code is ${err.code},message is ${err.message}`);
this.promptAction.showToast({ message: `数据库操作失败` });
});
} catch (err) {
this.promptAction.showToast({ message: `打开数据库出错` });
console.error(`getRdb failed, code is ${err.code},message is ${err.message}`);
}
}
async createTable(){
if (!this.rdbStore) {
this.promptAction.showToast({ message: `请先打开数据库` });
return;
}
const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS PERSON (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER)';
try {
await this.rdbStore?.executeSql(SQL_CREATE_TABLE);
this.promptAction.showToast({ message: `成功建表` });
} catch (error) {
this.promptAction.showToast({ message: `建表失败` });
console.error('建表失败')
}
}
}
更多关于HarmonyOS鸿蒙Next中Download/包名/目录下的db怎么才能访问的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
开发者您好,由于鸿蒙系统的安全设计,relationalStore不能直接对Download目录下的数据库文件进行读写操作,必须先复制到应用沙箱中,可以参考备份和恢复数据:
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/data-backup-and-restore#恢复手动备份数据
不建议直接把 /storage/Users/currentUser/Download/.../app.db 这样的公共目录绝对路径传给 relationalStore.getRdbStore。官方 RDB 文档描述的是在对应应用沙箱路径下创建或打开数据库;相关错误码也包括 invalid database path、access permission denied、unable to open database file。
公共 Download 属于用户文件区域,通常应通过 Picker/URI 或用户文件访问能力拿到访问权限,再把 db 文件复制到应用沙箱目录,例如 context.databaseDir 或应用可写目录,然后再用 getRdbStore 打开。推荐流程是:用户选择 db 文件或应用导入文件,复制到应用沙箱,StoreConfig.name 设置数据库文件名,必要时配置受支持的沙箱内目录,再创建或打开 RDB。
依据:
你好,参考如何使用公共目录下的数据库文件
需要使用公共目录下的数据库文件,主要有以下两种解决方案:
- 转存到应用沙箱内,完整使用该数据库文件:使用documentViewPicker.select()获取公共目录数据库文件,使用[@ohos.file.fs (文件管理)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-fs)的能力拷贝到沙箱目录后,即可使用[@ohos.data.relationalStore (关系型数据库)](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-data-relationalstore)对转存到沙箱目录的数据库文件进行完整操作。
- 使用@ohos.data.relationalStore (关系型数据库)的能力,配置rootDir、customDir使用只读模式打开该数据库文件:具体方案参照详细步骤。
你这个场景,本质上是:
relationalStore 不支持真正意义上的“公共目录数据库”
尤其是:
/storage/Users/currentUser/Download/xxx/app.db
这种路径。
———————— 很多人会误以为:
rootDir
可以随便指定绝对路径。
但实际上:
relationalStore 的底层有路径白名单限制
它设计目标本来就是:
应用数据库
默认只允许:
- 沙箱目录
- dataGroup
- 官方允许的数据目录
而不是:
公共 Download
———————— 你现在这个:
rootDir: "/storage/Users/currentUser/Download/com.xxx/"
虽然路径存在, 但:
SQLite 引擎初始化时会被系统拦截
所以:
getRdbStore()
直接打不开。
————————
重点:
customDir 不是任意目录
很多人踩坑。 它实际上是:
沙箱数据库目录下的相对路径
不是:
任意磁盘路径
官方实际行为类似:
context.databaseDir + '/rdb/' + customDir
所以:
customDir: "/storage/..."
本身就是无效的。 ————————
rootDir 也有限制
虽然 rootDir 看起来支持绝对路径, 但目前:
公共目录基本不可写
尤其:
- Download
- Documents
- Desktop
这种用户目录。 ————————
为什么?
因为:
relationalStore 不只是 SQLite 文件
它还涉及:
- WAL
- shm
- lock
- journal
- 安全等级
- 沙箱权限
- 数据组
系统不允许:
公共目录直接跑 relationalStore
否则权限体系会乱。 ————————
正确方案其实只有两个:
方案1(推荐)
不要直接在公共目录操作数据库
而是:
复制到沙箱
流程:
Download/app.db
↓
copy 到沙箱
↓
relationalStore 打开
↓
修改
↓
再 copy 回公共目录
这是目前最稳方案。 很多大型 App 其实也是这样。 ————————
方案2
放弃 relationalStore
直接:
Native SQLite
也就是:
- sqlite3_open()
- C/C++
- NAPI
直接操作:
/storage/Users/currentUser/Download/xxx/app.db
———————— 这个方案的好处:
你就是真正的 SQLite
没有 relationalStore 的路径限制。 因为:
你绕开了 ArkData
———————— 这个其实才适合你现在这种:
大量动态 db
场景。 因为你说:
每个文件夹都有相同名称 db
这种本来就不像:
应用主数据库
而更像:
外部数据文件
————————
你现在这个需求:
更适合:
sqlite3 + fileIo
不要用:
relationalStore
————————
另外还有一个坑:
公共目录权限。 即使你用 sqlite3, 也需要:
- picker 授权
或者 - SAF URI 权限
因为:
公共目录不是默认全权限
————————
总结一句:
你现在打不开:
/storage/Users/currentUser/Download/xxx/app.db
不是路径写错。 而是:
relationalStore 本身不支持把公共目录当数据库目录
你的场景正确方案应该是:
sqlite3 原生操作公共 db
或者:
copy 到沙箱后再 relationalStore 打开
给你提供一个思路,可考虑使用鸿蒙底层提供的 SQLite C API 或通过 Native API 直接操作数据库文件。但这需要熟悉 Native 开发(使用 C/C++),并在 ArkTS 中通过 FFI 调用,不过复杂度较高。
核心思路
利用 Node-API 实现 ArkTS 与 C/C++ 交互:Node-API 提供了一套稳定的 API,支持将 C/C++ 函数注册为 ArkTS 可调用的模块方法。你可以在 C/C++ 侧直接使用 SQLite 的原生 C 接口(如 sqlite3_open、sqlite3_exec等)对数据库文件进行读写。
绕过 relationalStore 的限制:通过 Native 模块直接操作文件路径,可以访问沙箱外的公共目录(如 Download/包名/下的 .db文件),但前提是已获得相应的存储权限。
Node-API简介-使用Node-API实现ArkTS/JS与C/C++语言交互-代码开发-NDK开发 - 华为HarmonyOS开发者
有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html
由于权限问题,你的这种设计场景应该是不允许的,db你只能放到沙箱中了
在HarmonyOS Next中,访问Download/包名/目录下的db文件需使用文件管理API。通过globalThis.getContext().getDatabaseDir()或getFilesDir()拼接目标路径,再调用fileIo.open或@ohos.data.relationalStore接口读取。注意应用沙箱内路径不可直接硬编码。
在HarmonyOS NEXT中,relationalStore 要求数据库文件必须在应用沙箱内(如 /data/storage/el2/base/preferences),无法直接将 rootDir 或 customDir 指向公共目录 Download。因此你的 StoreConfig 设置无论怎样修改路径都无法生效。
要实现读写 Download/包名/ 下的 db 文件,只能通过文件复制的方式:
- 使用 `` 将目标 db 文件复制到沙箱内某个临时路径(如
getContext().tempDir或databaseDir的子目录)。 - 用
relationalStore.getRdbStore打开沙箱内的副本,rootDir设为复制后的目录。 - 完成增删改查后,关闭数据库。
- 再用 `` 将沙箱内的 db 文件覆盖回原公共目录下的对应位置。
此操作需要申请的权限并通过动态授权(或通过用户选择授权持久化路径),但核心思路就是“沙箱内操作,读写完同步回原处”。如果 db 体积较大或需频繁修改,建议评估性能,或改用其他持久化方案。

