如何在类Web开发范式中使用File存储方式进行状态持久化?

简介: 如何在类Web开发范式中使用File存储方式进行状态持久化?

在类Web开发范式中,使用File存储方式进行状态持久化适合处理大量数据(如离线缓存、复杂JSON、二进制文件等)。鸿蒙提供了@ohos.fileio和应用上下文(Context)API用于文件操作,以下是详细实现步骤和示例:

一、核心API与前置知识

  1. 关键模块

    • @ohos.fileio:提供文件读写、创建、删除等操作。
    • @ohos.ability.featureAbility:获取应用沙箱目录(文件只能存储在应用私有目录,保障安全性)。
  2. 应用沙箱目录

    • getFilesDir():获取应用私有文档目录(数据会持久化,卸载应用后删除)。
    • 路径格式:/data/data/<应用包名>/files/(不同设备可能略有差异,无需硬编码)。
  3. 文件操作权限

    • 类Web范式中无需额外声明权限,应用默认拥有私有目录的读写权限。

二、完整实现步骤

1. 初始化:获取文件存储目录

首先需要获取应用的私有文件目录,所有文件操作都基于此目录:

// 导入必要模块
import featureAbility from '@ohos.ability.featureAbility';

// 获取应用私有文件目录(文档目录)
async function getAppFilesDir() {
   
  try {
   
    const context = featureAbility.getContext(); // 获取应用上下文
    const filesDir = await context.getFilesDir(); // 获取files目录路径
    return filesDir;
  } catch (error) {
   
    console.error('获取文件目录失败:', error.message);
    return null;
  }
}

2. 存储数据到文件(状态持久化)

将需要持久化的状态(如对象、数组)通过JSON.stringify()转为字符串,再写入文件:

import fileio from '@ohos.fileio';

/**
 * 保存数据到文件
 * @param {string} fileName 文件名(如"user_data.json")
 * @param {any} data 要保存的数据(对象、数组等)
 * @returns {Promise<boolean>} 是否保存成功
 */
async function saveDataToFile(fileName, data) {
   
  try {
   
    const filesDir = await getAppFilesDir();
    if (!filesDir) return false;

    // 拼接完整文件路径
    const filePath = `${
     filesDir}/${
     fileName}`;

    // 将数据转为字符串(二进制数据可直接传入Buffer)
    const content = typeof data === 'string' ? data : JSON.stringify(data);

    // 打开文件(0o2:写入权限;0o100:若文件不存在则创建)
    const fd = fileio.openSync(filePath, 0o2 | 0o100, 0o666); 
    // 0o666:文件权限(可读可写)

    // 写入内容
    fileio.writeSync(fd, content);

    // 关闭文件描述符(必须执行,否则会导致资源泄漏)
    fileio.closeSync(fd);

    console.log(`数据已保存到:${
     filePath}`);
    return true;
  } catch (error) {
   
    console.error('保存文件失败:', error.message);
    return false;
  }
}

使用示例:保存用户信息和商品列表

// 要持久化的状态数据
const appState = {
   
  userInfo: {
    id: 1, name: "鸿蒙用户", token: "xxx" },
  favoriteGoods: [ {
    id: 101, name: "手机" }, {
    id: 102, name: "平板" } ],
  lastLoginTime: new Date().toISOString()
};

// 调用保存方法
saveDataToFile('app_state.json', appState)
  .then(success => {
   
    if (success) console.log('状态保存成功');
  });

3. 从文件读取数据(恢复状态)

应用启动或需要时,从文件读取数据并解析为原始格式:

/**
 * 从文件读取数据
 * @param {string} fileName 文件名
 * @returns {Promise<any>} 读取的数据(对象/字符串等)
 */
async function readDataFromFile(fileName) {
   
  try {
   
    const filesDir = await getAppFilesDir();
    if (!filesDir) return null;

    const filePath = `${
     filesDir}/${
     fileName}`;

    // 打开文件(0o1:只读权限)
    const fd = fileio.openSync(filePath, 0o1);

    // 获取文件大小(用于创建合适的缓冲区)
    const stat = fileio.fstatSync(fd);
    const buffer = new ArrayBuffer(stat.size);

    // 读取文件内容到缓冲区
    const readSize = fileio.readSync(fd, buffer);

    // 关闭文件
    fileio.closeSync(fd);

    // 将缓冲区转为字符串并解析(二进制数据可直接返回Buffer)
    const content = String.fromCharCode.apply(null, new Uint8Array(buffer.slice(0, readSize)));

    // 尝试解析为JSON(非JSON数据直接返回字符串)
    try {
   
      return JSON.parse(content);
    } catch (e) {
   
      return content; // 非JSON格式直接返回字符串
    }
  } catch (error) {
   
    // 文件不存在时返回null(首次启动场景)
    if (error.code === 2) {
    // 2:文件不存在错误码
      console.log(`文件不存在:${
     fileName}`);
      return null;
    }
    console.error('读取文件失败:', error.message);
    return null;
  }
}

使用示例:应用启动时恢复状态

// 应用初始化时读取保存的状态
async function initAppState() {
   
  const savedState = await readDataFromFile('app_state.json');
  if (savedState) {
   
    console.log('恢复状态成功:', savedState);
    // 恢复到应用状态(如更新页面数据)
    this.setData({
   
      userInfo: savedState.userInfo,
      favorites: savedState.favoriteGoods
    });
  } else {
   
    console.log('无保存的状态,使用默认值');
    // 初始化默认状态
  }
}

// 在页面onInit生命周期调用
export default {
   
  onInit() {
   
    initAppState.call(this); // 注意绑定this上下文
  }
};

4. 其他常用操作(删除/更新文件)

(1)删除文件
/**
 * 删除文件
 * @param {string} fileName 文件名
 * @returns {Promise<boolean>} 是否删除成功
 */
async function deleteFile(fileName) {
   
  try {
   
    const filesDir = await getAppFilesDir();
    if (!filesDir) return false;

    const filePath = `${
     filesDir}/${
     fileName}`;
    fileio.unlinkSync(filePath); // 删除文件
    console.log(`文件已删除:${
     filePath}`);
    return true;
  } catch (error) {
   
    console.error('删除文件失败:', error.message);
    return false;
  }
}
(2)更新文件内容(覆盖写入)

更新操作与保存操作相同,fileio.openSync使用0o2权限会覆盖原有内容:

// 更新用户信息(覆盖原文件)
async function updateUserInfo(newUserInfo) {
   
  // 先读取现有状态
  const currentState = await readDataFromFile('app_state.json') || {
   };
  // 更新部分数据
  currentState.userInfo = newUserInfo;
  currentState.lastLoginTime = new Date().toISOString();
  // 覆盖写入
  return saveDataToFile('app_state.json', currentState);
}

三、最佳实践与注意事项

  1. 数据格式处理

    • 复杂对象/数组必须通过JSON.stringify()序列化,读取时用JSON.parse()还原。
    • 二进制数据(如图片、音频)可直接写入ArrayBuffer,无需序列化。
  2. 避免频繁写入

    • 高频更新的状态(如滚动位置)建议防抖处理(例如500ms内只写入一次),减少磁盘IO开销。
  3. 错误处理

    • 必须捕获文件操作异常(如磁盘满、文件权限不足),避免应用崩溃。
    • 文件不存在是正常场景(如首次启动),需返回默认状态。
  4. 清理过期数据

    • 定期删除不再需要的文件(如超过30天的缓存),可在应用启动时检查文件创建时间:
      // 检查文件是否过期(示例:7天有效期)
      async function isFileExpired(fileName, days = 7) {
             
        const filesDir = await getAppFilesDir();
        const fd = fileio.openSync(`${
               filesDir}/${
               fileName}`, 0o1);
        const stat = fileio.fstatSync(fd);
        fileio.closeSync(fd);
        const expireTime = new Date().getTime() - days * 24 * 60 * 60 * 1000;
        return stat.mtime.getTime() < expireTime; // mtime:最后修改时间
      }
      
  5. 大文件处理

    • 超过10MB的文件建议使用流式读写(fileio.createReadStream/createWriteStream),避免占用过多内存。

总结

使用File存储进行状态持久化的核心流程是:获取应用目录→序列化数据→写入文件,读取时则反向解析。这种方式适合存储大量或复杂状态,配合适当的错误处理和性能优化,可满足类Web开发范式中大部分持久化需求。相比Preferences,File存储更灵活但代码量略多,需根据实际数据规模选择。

相关文章
|
数据可视化 数据挖掘 BI
数据分析工具
【5月更文挑战第17天】数据分析工具
352 6
|
3月前
|
人工智能 IDE 开发工具
|
4月前
|
Linux
在线对Linux进行磁盘扩容的技术指南。
综上所述,Linux磁盘扩容的过程,重要的不仅是技术,更是对每一步骤的深刻理解和投入的爱心。只要手握正确的工具,我们不仅能满足"孩子"的成长需求,还能享受其中的乐趣和成就。
265 10
|
存储 弹性计算
阿里云服务器系统盘存储空间不够用怎么办?
当阿里云服务器系统盘空间不足时,您可以通过系统盘扩容或挂载数据盘解决。系统盘扩容无需重启服务器,详细步骤见系统盘扩容教程。挂载数据盘需预先购买,并确保与服务器位于同一地域和可用区,最多可挂载64块,详情见挂载数据盘教程
2251 6
|
小程序 搜索推荐 定位技术
解锁景区导览小程序,让每站旅程都精彩纷呈
【景区导览小程序】应对导览难、信息缺等挑战,提供电子地图、AR导航、个性化路线、智能讲解、景点打卡及AR互动等六项功能,提升游客游览效率与乐趣,促进景区形象升级,实现智慧旅游。游客轻松规划行程,享受沉浸式导航,通过互动分享带动景区流量增长。
451 0
解锁景区导览小程序,让每站旅程都精彩纷呈
阿里云地域节点测试IP(国内+海外)Ping值延迟测试
阿里云地域节点遍布全球19个地区节点(国内+海外),共有56个可用区,哪个快网分享阿里云国内及海外节点测试IP,阿里云节点Ping值延迟测试: 阿里云节点全球无缝覆盖,提供CN2高速网络,BGP接入支持。
39041 0
|
Linux
Linux——系统扩容根目录磁盘空间、磁盘扩容的操作方法
Linux根目录磁盘空间不够用了,当修改了虚拟机模版增加磁盘大小或者插入了一块新硬盘,但是发现系统里的大小还是没改变。 产生的原因是没有给磁盘格式化,没有增加分区。
4289 0
|
网络架构
rest参数
rest参数
287 0
|
应用服务中间件 Linux Serverless
搭建Vue3组件库:第十四章 使用Vercel部署在线文档
介绍如何使用Vercel来部署我们的在线文档
650 0
搭建Vue3组件库:第十四章 使用Vercel部署在线文档
|
安全 算法 NoSQL
快速搭建平头哥安全处理器的可信执行环境
平头哥安全处理器系列包括CK802T, CK803T, CK804T和E902T等, 结合平头哥SoC安全机制,搭建的芯片平台具有良好的兼容性和健壮性。基于平头哥TEE OS接口的扩展性和移植性,可以方便地快速地移植到其他平头哥安全处理器的芯片平台上。本文介绍了如何在不同的芯片平台快速创建可信执行环境的步骤和注意点,帮助设计开发者迅速的在平头哥安全处理器上开发TEE OS。
946 0
快速搭建平头哥安全处理器的可信执行环境