HarmonyOS 鸿蒙Next中sqlite的DB文件的拷贝

HarmonyOS 鸿蒙Next中sqlite的DB文件的拷贝 如何将已有sqlite的db文件拷贝到沙箱里(鸿蒙app默认文件路径)。

能否提供一个完整例子。

开发环境:HarmonyOS 6.0.0 Release SDK,原样包含OpenHarmony SDK Ohos_sdk_public 6.0.0.47 (API Version 20 Release)

8 回复

开发者你好,参考文档将rawfile文件夹下的db文件推到数据存储沙箱路径

【解决方案】

  1. 将db文件推送到数据存储沙箱路径:/data/app/el2/100/database/(bundleName)/entry/rdb/。实现方式为使用文件管理接口打开本地数据库,读取其内容并写入沙箱路径下的db文件中。

    import { fileIo } from '@kit.CoreFileKit';
    import { relationalStore } from '@kit.ArkData';
    import { common } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    
    // Obtaining the Context in EntryAbility, save it to AppStorage, then use AppStorage to retrieve it in the utility class.
    let context = AppStorage.get('context') as UIContext;
    let UiAbilityContent = context.getHostContext() as common.UIAbilityContext;
    let RDBDirectory = UiAbilityContent.databaseDir;
    let resource = UiAbilityContent.resourceManager;
    
    function initDatabase() {
      // Create a database sandbox directory
      try {
        let dirPath = RDBDirectory + '/entry';
        fileIo.mkdirSync(dirPath);
        dirPath = dirPath + '/rdb';
        fileIo.mkdirSync(dirPath);
      } catch (error) {
        console.error(`mkdir rdbPath failed, error code: ${error.code}, message: ${error.message}.`);
      }
    
      // Set db name
      let dbName: string = 'Objective.db';
    
      // Read the db file in the rawfile directory
      try {
        let content = resource.getRawFileContentSync(dbName);
        let cFile = RDBDirectory + '/entry/rdb/' + dbName;
        let cacheFile = fileIo.openSync(cFile, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
        fileIo.write(cacheFile.fd, content.buffer);
        fileIo.closeSync(cacheFile.fd);
      } catch (error) {
        console.error(`callback getRawFd failed, error code: ${error.code}, message: ${error.message}.`);
      }
    }
    
  2. 通过getRdbStore获取保存在沙箱路径下的db文件

    async function getRDB(): Promise<relationalStore.RdbStore | undefined> {
      let result: relationalStore.RdbStore | undefined = undefined;
      const STORE_CONFIG: relationalStore.StoreConfig = {
        name: 'Objective.db',
        securityLevel: relationalStore.SecurityLevel.S1
      };
    
      await relationalStore.getRdbStore(UiAbilityContent, STORE_CONFIG).then((rdbStore: relationalStore.RdbStore) => {
        result = rdbStore;
        console.info('Get RdbStore successfully.');
      }).catch((err: BusinessError) => {
        console.error(`Get RdbStore failed, code is ${err.code}, message is ${err.message}`);
      });
      return result;
    }
    

更多关于HarmonyOS 鸿蒙Next中sqlite的DB文件的拷贝的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在鸿蒙(HarmonyOS)开发中,将预置的 SQLite 数据库文件(.db)拷贝到应用1沙箱路径的操作需要遵循特定的安全规范。以下是完整解决方案和代码示例,适用于 HarmonyOS 6.0.0 (API Version 20):

核心步骤说明

  1. 沙箱路径规定 数据库文件必须放在应用沙箱路径: /data/app/el2/100/database/<bundleName>/entry/rdb/或其子目录下(234)

  2. 最佳实践 将预置的 .db 文件放在 resources/rawfile目录,通过文件操作 API 移动到沙箱路径


完整代码实现

// 文件操作模块
import fs from '@kit.ArkFileIO';
// 关系型数据库模块
import relationalStore from '@kit.ArkData';

async function copyDbToSandbox(context: common.Context) {
  try {
    // 1. 定义路径
    const srcPath = 'resource://rawfile/your_db.db'; // rawfile中的源文件
    const destDir = context.databaseDir + '/rdb/'; // 沙箱数据库目录
    const destPath = destDir + 'your_db.db';

    // 2. 创建目标目录
    await fs.ensureDir(destDir);

    // 3. 复制文件
    const srcFile = await fs.open(srcPath, fs.OpenMode.READ_ONLY);
    const destFile = await fs.open(destPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
    
    const stat = await fs.stat(srcPath);
    const buffer = new ArrayBuffer(stat.size);
    await fs.read(srcFile.fd, buffer);
    await fs.write(destFile.fd, buffer);
    
    await fs.close(srcFile.fd);
    await fs.close(destFile.fd);

    console.log('Database copied to sandbox');

    // 4. 初始化数据库连接
    const storeConfig: relationalStore.StoreConfig = {
      name: destPath,
      encrypt: false // 若需加密设为true并配置密钥
    };
    
    relationalStore.getRdbStore(context, storeConfig, (err, store) => {
      if (err) {
        console.error(`Database init failed: ${err.message}`);
      } else {
        console.log('Database connection opened');
        // 执行数据库操作...
      }
    });

  } catch (error) {
    console.error(`File operation error: ${error.message}`);
  }
}

关键注意事项

  1. 路径权限

    • 只能访问 context.databaseDir指定的沙箱路径(4)
    • 直接访问设备其他目录会被系统拒绝
  2. 加密配置 若需加密数据库,在 StoreConfig中设置: encrypt: true, securityLevel: relationalStore.SecurityLevel.S1 // 安全级别

  3. 文件完整性

    • 避免直接操作 .db 文件,应使用 backup()/restore()API()
    • 复制后可通过 fs.hash()验证文件完整性

调用示例

在 Ability 的 onCreate方法中调用:

import UIAbility from '@ohos.app.ability.UIAbility';

export default class EntryAbility extends UIAbility {
  onCreate() {
    copyDbToSandbox(this.context).catch(console.error);
  }
}

重要提示

  1. 实际路径中的 <bundleName>会自动替换为应用包名
  2. 若使用加密数据库,需保证加密/解密参数完全一致(23)
  3. 此方案适用于 API Version 20+,兼容 HarmonyOS 6.0.0 SDK

信息来源

问答专区-华为/鸿蒙开发者论坛-华为开发者联盟

预置的SQLite数据库如何安全存储,防止反编译泄露

预置在rawfile文件夹下的SQLite数据库db文件,如何进行安全存储,防止反编译泄露

HarmonyOS 下载sqlite数据库文件到APP本地,再在APP里面访问数据库报错 (API12+)

谢谢。大佬。 EntryAbility.ts导入 上面的代码 @kit.ArkFileIO 识别不到。出下面的错误 Cannot find module ‘@kit. ArkFileIO’ or its corresponding type declarations. <ArkTSCheck>,

开发者你好,没有@kit.ArkFileIO这个kit,文件管理是CoreFileKit。导入语句为:import { fileIo as fs } from ‘@kit.CoreFileKit’;

下面代码演示了将 resoutces/rawfile/ 目录下的db文件 拷贝到沙箱文件目录,db文件命名相同.

// 将 resoutces/rawfile/mysqlite.db 拷贝到沙箱目录
private async copyDBToSandboxPath() {
    try {
      const context = this.getUIContext().getHostContext()
      const path = context?.filesDir + '/mysqlite.db'
      const buf = await context?.resourceManager.getRawFileContent('mysqlite.db')
      const outFile =
        await fileIo.open(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY | fileIo.OpenMode.TRUNC)
      await fileIo.write(outFile.fd, buf?.buffer)
      await fileIo.close(outFile.fd)
    } catch (e) {
    }
  }

使用Docker部署MySQL

1. 拉取MySQL镜像

docker pull mysql:8.0

2. 创建数据卷

docker volume create mysql_data
docker volume create mysql_conf

3. 创建自定义配置文件

在宿主机上创建配置文件目录并添加 my.cnf 文件:

mkdir -p /docker/mysql/conf

编辑 /docker/mysql/conf/my.cnf

[mysqld]
user=mysql
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default_authentication_plugin=mysql_native_password

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

4. 运行MySQL容器

docker run -d \
  --name mysql \
  --restart always \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=your_password \
  -v mysql_data:/var/lib/mysql \
  -v /docker/mysql/conf:/etc/mysql/conf.d \
  mysql:8.0

参数说明:

  • -d:后台运行
  • --name:容器名称
  • --restart always:自动重启
  • -p:端口映射(宿主机端口:容器端口)
  • -e:环境变量(设置root密码)
  • -v:挂载数据卷和配置文件

5. 常用Docker命令

# 查看容器状态
docker ps -a

# 查看容器日志
docker logs mysql

# 进入容器
docker exec -it mysql bash

# 停止容器
docker stop mysql

# 启动容器
docker start mysql

# 删除容器
docker rm mysql

6. 连接MySQL

# 进入容器连接
docker exec -it mysql mysql -uroot -p

# 或从外部连接
mysql -h 127.0.0.1 -P 3306 -uroot -p

注意事项

  1. your_password 替换为实际的强密码
  2. 生产环境建议使用更复杂的密码策略
  3. 数据卷确保数据持久化
  4. 配置文件可根据实际需求调整

鸿蒙Next中SQLite数据库文件拷贝可通过FileManager API实现。使用ohos.file.fs模块的copyFile方法直接复制源文件到目标路径。需注意应用沙箱权限,源文件需位于应用可访问目录,目标路径需有写入权限。操作前应关闭数据库连接,避免文件锁定导致复制失败。

在HarmonyOS Next中,将预置的SQLite数据库文件从应用安装包(resources/rawfile目录)拷贝到应用沙箱路径,是实现数据初始化的常见需求。以下是一个基于API Version 20的完整示例,展示了如何安全、高效地完成此操作。

核心步骤与代码示例

  1. 放置数据库文件:将你的预置数据库文件(例如 preload.db)放入项目的 resources/rawfile 目录下。

  2. 实现拷贝逻辑:在应用启动时(例如在 EntryAbilityonWindowStageCreate 中)或需要时调用以下方法。

import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';

async function copyPreloadedDatabase(context: common.UIAbilityContext): Promise<void> {
  // 1. 定义目标沙箱路径
  const sandboxDir: string = context.filesDir;
  const targetDbPath: string = sandboxDir + '/myapp.db'; // 你的目标数据库文件名

  // 2. 检查目标文件是否已存在,避免重复拷贝
  try {
    await fs.access(targetDbPath);
    console.info('Database already exists at target path.');
    return;
  } catch (error) {
    // 文件不存在,继续执行拷贝
    console.info('Target database does not exist, proceeding with copy.');
  }

  // 3. 从rawfile读取源文件
  const sourceDbFile: fs.File = await context.resourceManager.getRawFile('preload.db');
  const stat: fs.Stat = await fs.stat(sourceDbFile.fd);
  const fileSize: number = stat.size;

  // 4. 创建读取流(源文件)和写入流(目标文件)
  const readStream: fs.Stream = fs.createStreamSync(sourceDbFile.fd, 'r');
  const writeStream: fs.Stream = fs.createStreamSync(targetDbPath, 'w');

  // 5. 使用流式拷贝,适合大文件,避免内存压力
  const bufferSize: number = 4096; // 4KB缓冲区
  let totalBytesRead: number = 0;

  while (totalBytesRead < fileSize) {
    const readSize: number = Math.min(bufferSize, fileSize - totalBytesRead);
    const buffer: ArrayBuffer = new ArrayBuffer(readSize);
    const bytesRead: number = await readStream.read(buffer);

    if (bytesRead > 0) {
      await writeStream.write(buffer);
      totalBytesRead += bytesRead;
    } else {
      break;
    }
  }

  // 6. 关闭流并同步文件确保数据写入磁盘
  readStream.closeSync();
  writeStream.closeSync();
  await fs.fsync(writeStream.fd);

  console.info('Database copied successfully to: ' + targetDbPath);
}

// 在Ability中调用示例
export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // ... 其他初始化代码
    copyPreloadedDatabase(this.context).then(() => {
      console.info('Database copy process completed.');
      // 此处可以初始化你的数据库连接
    }).catch((err: Error) => {
      console.error('Failed to copy database: ' + JSON.stringify(err));
    });
  }
}

关键点说明

  • 目标路径context.filesDir 指向应用沙箱的文件目录,数据在此处可读写且对其他应用私有。
  • 流式操作:使用 fs.createStreamSync 创建读写流进行分块拷贝,这对于较大的数据库文件至关重要,可以避免一次性加载整个文件到内存中。
  • 存在性检查:拷贝前使用 fs.access 检查目标文件是否存在,防止每次启动都重复覆盖。
  • 数据完整性:拷贝完成后调用 fs.fsync 确保所有数据从系统缓冲区刷新到物理存储。
  • 错误处理:示例中包含基本的错误处理,在实际开发中应根据需要进一步细化。

后续使用

拷贝完成后,你可以使用 @ohos.data.relationalStore@ohos.data.rdb 等HarmonyOS数据管理API,通过目标路径 targetDbPath 来打开并操作这个数据库。

此方法直接、高效,是HarmonyOS Next中处理预置数据库文件的推荐方式。

回到顶部