【Harmoney OS 鸿蒙应用开发】将rawfile中文件内容保存到本地数据库中

rawfile文件内容保存到本地数据库

不知道各位在平时开发过程中是否有遇到过这样一个场景:在开发一个app的时候,有些数据是需要经过接口请求获取到的,但是用户在使用app的时候会遇到没有网络的情况,那么他这个时候就不能够获取到数据,页面显示也就是空白的,会导致用户体验感变差。那么,要如何处理这种情况呢?跟随本文一起学习一下吧......

关于rawfile

rawfile目录主要作用是:在鸿蒙操作系统中,rawfile目录一般用于存放原始资源文件。

一般在该目录中存放的本地文件类型为html类型或者txt类型。

如果需要在同一个模块中引用文件使用,可以通过$rawfile 函数直接引用这些文件的路径(相对于 rawfile 目录)。

例如:在rawfile目录中有一个index.html文件,使用web组件引用它:

Web({ src: $rawfile('index.html'), controller: this._controller })

但是这种通过$rawfile函数引用的方式,只可以引用文件,并不能获取到文件中的内容;例如html中的富文本还有txt中的文本。

如果需要获取到文件中的内容,需要使用到一个新的资源管理API——resourceManager,可以根据当前的配置提供获取应用资源信息读取。

获取rawfile文件内容

对于如何在rawfile文件内容,在resourceManager中有一个方法可以读取,getRawFileContentSync:用户获取resources/rawfile目录下对应的rawfile文件内容,使用同步形式返回。

注意:从API Version9之前使用resourceManager需要通过导入模块,在API Version9开始,在Stage模型中可以通过context获取到resourceManager对象的方式,直接调用其内部获取资源的接口。

通过getRawFileContentSync方法可以获取到文件内容,返回的是Uint8Array字节流类型数据:

getRawfile(name: string) {
    const data = getContext(this).resourceManager.getRawFileContentSync(`${name}.txt`) //data是字节流类型数据
    console.log(JSON.stringify(data))
    return data
  }

原本数据就只有4个文字:

打印出来的数据:

将内容写入沙箱

因为获取到的字节流类型数据,不能直接存储到数据库中使用,所以需要先写入沙箱经过转换后再从沙箱中获取内容。

fs文件管理

要将文件内容写入沙箱就需要用到文件管理这个API,该模块为基础模块操作API,提供基础文件操作能力,包括文件基本管理、文件目录管理、文件信息统计、文件流式读写等常用功能。

注意:在使用该模块对文件/目录进行操作之前,需要先获取其应用沙箱路径。

将本地rawfile目录中的文件数据存储到沙箱:

//   将本地rawfile目录中的文件数据存储到沙箱
  getRawfile(name: string): string {
    const data = getContext(this).resourceManager.getRawFileContentSync(`${name}.txt`) //data是字节流类型数据
    // console.log(JSON.stringify(data))
    const myBuffer: ArrayBufferLike = data.buffer
    //获取上下文
    const context = getContext(this)
    //创建文件
    const filePath = context.filesDir + `/${name}.txt`
    // 检查文件是否存在
    const res = fs.accessSync(filePath)
    // 表示文件存在,由于文件存在如果再写入一份数据,长度不一致的话会导致错乱,需要先删除文件
    if (res) {
      fs.unlinkSync(filePath)
    }
    // 以可读可写模式打开,如果没有文件则会创建文件
    const file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    // 将字节流数据写入文件
    fs.writeSync(file.fd, myBuffer)
    // 读取文件内容,是字符串类型
    const Str = fs.readTextSync(filePath)
    // AlertDialog.show({ message: Str })
    // 必须关闭文件,否则文件可能会一直执行,导致资源崩溃
    fs.closeSync(file)
    return Str
  }

这里有两个需要注意的地方:第一就是在使用fs写入文件之前,需要判断该文件是否存在,否则会出现由于写入内容字节长度不一致导致的内容错乱。例如:

第一次写入内容:我是中国人,我自豪

输出结果:我是中国人,我自豪

第二次写入内容:我是帅哥

输出结果:我是帅哥人,我自豪

第三次输入内容:我是一个182cm大帅哥

输出结果:我是一个182cm大帅哥

这是因为在同一个文件中进行多次写入,并不会覆盖原有内容或者继续在后面增加内容,而是会通过字节形式从头开始一个字节一个字节对应覆盖,如果比上一次写入的内容短,那么就会出现第二次输出结果,如果比上一次写入的内容长,那么就会完全覆盖跟正常显示一样。

有人会疑惑为什么不每次写入文件都重新生成一个文件,在新的文件中写入;确实,这样的话在每个文件中每次写入的内容都可以完整显示,不会出现内容错乱,但是有个问题就是每次写入都生成一个文件会导致沙箱文件积多,占用缓存;并且在之后将文件内容导入数据库也会困难,因为太多文件名了,不好处理。

第二个需要注意的地方,在使用文件的时候,使用open打开过文件之后一定要记得使用close关闭文件,否则文件可能会一直执行,导致资源崩溃。博主在开发过程中不经意间出现过一次,找了很久,控制台崩溃了一直打印不出东西,最后重启ide才发现的。o(╥﹏╥)o

数据库管理

关系型数据库

关系型数据库(Relational Database,RDB)是一种基于关系模型来管理数据的数据库。关系型数据库基于SQLite组件提供了一套完整的对本地数据库进行管理的机制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL语句来满足复杂的场景需要。

注意:为保证插入并读取数据成功,建议一条数据不要超过2M。超出该大小,插入成功,读取失败。

该模块提供的常用功能:

  • RdbPredicates:数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。
  • RdbStore:提供管理关系数据库(RDB)方法的接口。
  • ResultSet:提供用户调用关系型数据库查询接口之后返回的结果集合。

获取管理数据库的对象

// 获取管理数据库的对象
  async getStoreInstance() {
    try {
      // 获取 store
      const store = await relationalStore.getRdbStore(getContext(), {
        name: cityData + '.db', // 数据库文件名,如果文件不存在会自动创建,已存在则不创建
        securityLevel: relationalStore.SecurityLevel.S4 // 安全等级
      })
      // 执行创建表的语句(如果不存在才创建表,已存在则不创建)
      store.executeSql(`CREATE TABLE IF NOT EXISTS cityData (
                          id INTEGER PRIMARY KEY AUTOINCREMENT,
                          name TEXT,
                          filePath TEXT,
                          date_added INTEGER,
                          content TEXT
                      )`)
      // 返回 store
      return store
    } catch (err) {
      return Promise.reject('initDB error')
    }
  }

对数据库的操作一般都只有新增、更新、删除、查询、查询数量等等......

但是本场景开发只用到了新增、更新、查询、删除,更多方法可以参考RdbStore

节省时间,我就将从rawfile获取内容写入沙箱,到获取数据库实例,以及对数据库的增删改查等操作封装在一起:

import { relationalStore, ValuesBucket } from '@kit.ArkData'
import fs from '@ohos.file.fs';

// 文件字段
export interface PageDBInfo extends ValuesBucket {
  id: number | null // 新增时 id 为 null ,自动生成自增 id
  name: string //文件名
  filePath: string //沙箱文件路径
  date_added: number //添加日期
  content: string //内容,也就是页面数据
}

export class fileDB {
  private store: relationalStore.RdbStore | null = null
  private tableName = 'cityData' //表名
  //   创建数据库的语句
  private sqlCreate = `CREATE TABLE IF NOT EXISTS ${this.tableName} (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT,
  filePath TEXT,
  date_added INTEGER,
  content TEXT
  )`

  //   将本地rawfile目录中的文件数据存储到沙箱
  getRawfile(name: string): string {
    const data = getContext(this).resourceManager.getRawFileContentSync(`${name}.txt`) //data是字节流类型数据
    // console.log(JSON.stringify(data))
    const myBuffer: ArrayBufferLike = data.buffer
    //获取上下文
    const context = getContext(this)
    //创建目录
    // const fileDIr = context.filesDir + '/city'
    // fs.mkdirSync(fileDIr)
    //创建文件
    const filePath = context.filesDir + `/${name}.txt`
    // 检查文件是否存在
    const res = fs.accessSync(filePath)
    // 表示文件存在,由于文件存在如果再写入一份数据,长度不一致的话会导致错乱,需要先删除文件
    if (res) {
      fs.unlinkSync(filePath)
    }
    // 以可读可写模式打开,如果没有文件则会创建文件
    const file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    // 将字节流数据写入文件
    fs.writeSync(file.fd, myBuffer)
    // 读取文件内容,是字符串类型
    const Str = fs.readTextSync(filePath)
    // AlertDialog.show({ message: Str })
    // 必须关闭文件,否则文件可能会一直执行,导致资源崩溃
    fs.closeSync(file)
    return Str
  }

  // 获取管理数据库的对象
  async getStoreInstance() {
    try {
      // 如果 store 已存在,直接返回 store
      if (this.store) {
        return this.store
      }
      // 获取 store
      const store = await relationalStore.getRdbStore(getContext(), {
        name: this.tableName + '.db', // 数据库文件名,如果文件不存在会自动创建,已存在则不创建
        securityLevel: relationalStore.SecurityLevel.S4 // 安全等级
      })
      // 执行创建表的语句(如果不存在才创建表,已存在则不创建)
      store.executeSql(this.sqlCreate)
      // 存储 store 用于下次快速获取
      this.store = store
      // 返回 store
      return store
    } catch (err) {
      return Promise.reject('initDB error')
    }
  }

  //   新增
  async insert(value: PageDBInfo) {
    //   获取数据库对象
    const store = await this.getStoreInstance()
    // 核心代码 insert,返回行的id
    const rowId = await store.insert(this.tableName, value)
    return rowId > -1 ? Promise.resolve(rowId) : Promise.reject('insert error')
  }

  //   更新
  async update(item: PageDBInfo) {
    // 如果该行数据不存在id,返回错误
    if (!item.id) {
      return Promise.reject('id error')
    }
    //   获取数据库对象
    const store = await this.getStoreInstance()
    // 获取数据库的谓词
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    // 匹配数据表中name列中值为item.name的字段数据
    predicates.equalTo('name', item.name)
    // 核心代码 update,返回受影响的行数
    const rowCount = await store.update(item, predicates)
    return rowCount ? Promise.resolve(rowCount) : Promise.reject('update error')
  }

  // 删除
  async delete(name: string) {
    //   获取数据库对象
    const store = await this.getStoreInstance()
    // 获取数据库的谓词
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('name', name)
    // 核心代码 delete,返回删除的行数
    const rowCount = await store.delete(predicates)
    return rowCount ? Promise.resolve(rowCount) : Promise.reject('delete error')
  }

  //   查询
  async query(name: string) {
    try {
      const store = await this.getStoreInstance()
      const predicates = new relationalStore.RdbPredicates(this.tableName)
      // 根据 id 倒序排列(大到小)
      predicates.orderByDesc('id')
      // 添加name到条件中
      predicates.equalTo('name', name)
      // 核心代码 query
      const resultSet = await store.query(predicates)
      // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
      let content = ''
      while (resultSet.goToNextRow()) {
        // 获取到内容,为字符串类型
        content = resultSet.getString(resultSet.getColumnIndex('content'))
      }
      //   关闭结果集,释放内存空间,若不释放可能会引起fd泄露与内存泄露
      resultSet.close()
      //   返回结果
      return content
    } catch (err) {
      return '查询失败'
    }
  }
}

export const FileDB = new fileDB()

需要注意的是:在查询方法中query方法返回的resultSet结果集是一个数据集合的游标,默认指向第-1个记录,有效的数据需要从0开始。还有最后需要通过结果集的close方法关闭结果集,释放内存空间,若不释放肯呢个会引起fd泄露以及内存泄露。

将rawfile文件内容保存到数据库

rawfile目录中文件内容:

写入到沙箱中的内容:

沙箱文件中内容一致,表示写入成功:

将数据插入到数据库中,显示内容:

查询数据库中该数据信息,控制台已经弹窗可以打印出内容:

点击删除之后返回删除的行数为1,再点击查询内容为空白,没有输出:

将rawfile文件中内容保存到本地数据库的方法基本就叙述完了,感谢查阅。

如果觉得对您有帮助,烦请点赞收藏哦,ღ( ´・ᴗ・` )比心

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值