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

10 回复

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。

依据:

你好,参考如何使用公共目录下的数据库文件

需要使用公共目录下的数据库文件,主要有以下两种解决方案:

你这个场景,本质上是:

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),无法直接将 rootDircustomDir 指向公共目录 Download。因此你的 StoreConfig 设置无论怎样修改路径都无法生效。

要实现读写 Download/包名/ 下的 db 文件,只能通过文件复制的方式:

  1. 使用 `` 将目标 db 文件复制到沙箱内某个临时路径(如 getContext().tempDirdatabaseDir 的子目录)。
  2. relationalStore.getRdbStore 打开沙箱内的副本,rootDir 设为复制后的目录。
  3. 完成增删改查后,关闭数据库。
  4. 再用 `` 将沙箱内的 db 文件覆盖回原公共目录下的对应位置。

此操作需要申请的权限并通过动态授权(或通过用户选择授权持久化路径),但核心思路就是“沙箱内操作,读写完同步回原处”。如果 db 体积较大或需频繁修改,建议评估性能,或改用其他持久化方案。

回到顶部