尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

数据的身家性命:ArkTS 为鸿蒙备份导出设计目标库结构

数据的身家性命:ArkTS 为鸿蒙备份导出设计目标库结构 实例数据备份导出Backup技术备份目标库设计、BackupDao 封装一、业务需求分析备份导出的本质数据是应用的「身家性命」——用户记的账、写的日记、存的联系人丢了就是灾难。备份导出Backup/Restore是数据治理的核心能力业务需求拆解导出把数据源表的数据序列化成 JSON / CSV 文本写入应用沙箱文件记录备份每次导出生成一条备份日志文件名、大小、时间、格式可追溯恢复导入读取备份内容清空数据源表后重新导入事务保证原子删除备份删除备份记录 沙箱文件双删除终端化体验页面用「终端控制台」风格展示操作日志——像黑客帝国一样酷。技术栈本实例首次引入文件读写kit.CoreFileKit的fileIo——数据库能力 文件系统的结合让「数据离开数据库变成文件再从文件回到数据库」的闭环成立。二、双表结构设计备份日志表 数据源表备份日志表 backup_log字段名类型约束说明idINTEGERPRIMARY KEY AUTOINCREMENT自增主键file_nameTEXTNOT NULL备份文件名backup_时间戳.jsonsizeINTEGERNOT NULL DEFAULT 0文件大小字节source_tableTEXTNOT NULL数据源表名backup_timeINTEGERNOT NULL备份时间戳typeTEXTNOT NULL DEFAULT ‘json’格式json / csv数据源表 backup_source字段名类型约束说明idINTEGERPRIMARY KEY AUTOINCREMENT自增主键nameTEXTNOT NULL条目名称categoryTEXTDEFAULT ‘’分类amountREALNOT NULL DEFAULT 0金额noteTEXTDEFAULT ‘’备注created_timeINTEGERNOT NULL创建时间戳设计要点拆解1. backup_log 是「备份的元数据」。它不存备份内容内容在沙箱文件里只存「哪次备份、什么文件、多大、何时」——是文件系统的索引表。元数据与内容分离内容在文件可大可小索引在数据库查询快。2. backup_source 是「被备份的业务数据」。模拟一个真实的业务表比如「资产清单」华为手机、蓝牙耳机、运动鞋…12 条数据用于导出/恢复演示。实际项目中它可以是任何业务表联系人、账单、日记——本实例用它代表「任意可备份的数据源」。3. type 区分格式。json / csv 两种导出格式backup_time 时间戳排序备份历史。4. source_table 字段的扩展意义。记录「备份的是哪张表」——如果未来备份多张表联系人表 账单表这个字段让日志可区分来源。为多表备份预留是设计前瞻。三、建表 SQLCREATETABLEIFNOTEXISTSbackup_log(idINTEGERPRIMARYKEYAUTOINCREMENT,file_nameTEXTNOTNULL,sizeINTEGERNOTNULLDEFAULT0,source_tableTEXTNOTNULL,backup_timeINTEGERNOTNULL,typeTEXTNOTNULLDEFAULTjson);CREATETABLEIFNOTEXISTSbackup_source(idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL,categoryTEXTDEFAULT,amountREALNOTNULLDEFAULT0,noteTEXTDEFAULT,created_timeINTEGERNOTNULL);两张表都无需额外索引backup_log 按时间倒序查询可接受全表扫描数据量小backup_source 全量读取。四、BackupDao 封装从数据库到文件的桥梁数据层核心BackupDao本实例第一次引入fileIo文件 IOimport{fileIo}fromkit.CoreFileKit;实体接口导出结构exportinterfaceBackupRecord{id:number;fileName:string;size:number;sourceTable:string;backupTime:number;type:string;}exportinterfaceSourceRecord{id:number;name:string;category:string;amount:number;note:string;createdTime:number;}BackupRecord对应 backup_log 表SourceRecord对应 backup_source 表也是 JSON 导出的行结构。五、数据源查询导出前的读取导出第一步是从数据源表读出全部记录staticasyncquerySource(context:common.Context):PromiseSourceRecord[]{conststoreawaitBackupDao.getStore(context);constresultawaitstore.querySql(SELECT * FROM${BackupDao.SOURCE_TABLE}ORDER BY created_time DESC);constlist:SourceRecord[][];while(result.goToNextRow()){constr:SourceRecord{id:result.getLong(result.getColumnIndex(id)),name:result.getString(result.getColumnIndex(name)),category:result.getString(result.getColumnIndex(category))||,amount:result.getDouble(result.getColumnIndex(amount)),note:result.getString(result.getColumnIndex(note))||,createdTime:result.getLong(result.getColumnIndex(created_time)),};list.push(r);}result.close();returnlist;}查询结果就是「待序列化的数据」——下一步把它转成 JSON / CSV 字符串。六、序列化JSON 与 CSV 的生成JSON 导出——JSON.stringify一把梭staticasyncexportJson(context:common.Context):Promisestring{constrowsawaitBackupDao.querySource(context);returnJSON.stringify(rows);}JSON.stringify(rows)的产出[{id:1,name:华为 Mate 60 Pro,category:数码,amount:6999,...},...]——数组序列化为 JSON 文本可读、可恢复。CSV 导出——手工拼接带表头staticasyncexportCsv(context:common.Context):Promisestring{constrowsawaitBackupDao.querySource(context);letcsvid,name,category,amount,note,created_time\n;for(constrofrows){csv${r.id},${r.name},${r.category},${r.amount},${r.note},${r.createdTime}\n;}returncsv;}CSV 格式首行表头id,name,…每行一条记录逗号分隔。CSV 的优势是可以被 Excel 直接打开——用户导出后双击即看这是比 JSON 更「用户友好」的格式。两种格式满足两种需求JSON 给程序可恢复CSV 给人可查看。CSV 的潜在坑如果字段值本身含逗号或换行如备注「a,b」简单拼接会破坏列结构。本实例数据不含逗号直接拼接够用生产环境应对字段做引号包裹${value}转义。七、文件写入fileIo 的打开-写入-关闭序列化后的文本要落盘到应用沙箱staticasyncwriteFile(context:common.Context,fileName:string,content:string):PromiseFileWriteResult{constfilesDircontext.filesDir;// 应用沙箱文件目录constpath${filesDir}/${fileName};constfilefileIo.openSync(path,fileIo.OpenMode.READ_WRITE|fileIo.OpenMode.CREATE|fileIo.OpenMode.TRUNC);fileIo.writeSync(file.fd,content);fileIo.closeSync(file);conststatfileIo.statSync(path);constr:FileWriteResult{path:path,size:stat.size};returnr;}fileIo 三件套调用作用openSync(path, mode)打开文件返回 file 对象含 fd 文件描述符writeSync(fd, content)写入内容closeSync(file)关闭文件必须释放句柄OpenMode 组合READ_WRITE | CREATE | TRUNC——可读写、不存在则创建、已存在则截断清空。TRUNC 保证每次写入都是「全新文件」而非追加残留。context.filesDir应用沙箱的文件目录如/data/app/el2/100/base/com.example.xiangcejihe/haps/entry/files——每个应用独立的私有目录写文件不需要任何权限沙箱内自由读写。沙箱文件是应用私有数据其他应用无法访问安全有保障。statSync(path).size写完后取文件大小字节——用于 backup_log 的 size 字段日志记录「这次备份多大」。八、技术要点对照表技术点实现方式生产价值元数据分离backup_log 索引 沙箱文件内容日志查询快JSON 导出JSON.stringify(rows)程序可恢复CSV 导出表头 行拼接Excel 可打开文件写入openSync/writeSync/closeSync沙箱落盘OpenModeREAD_WRITE|CREATE|TRUNC截断重写沙箱目录context.filesDir免权限私有存储九、文章小结备份实例建立了**「数据库 文件」双栖架构**数据源表存业务数据序列化JSON/CSV导出到沙箱文件backup_log 记录备份元数据。技术新能力是 fileIo 文件读写open/write/close 三件套 OpenMode 组合 filesDir 沙箱与数据库操作组合成「导出 → 落盘 → 记录」的完整链路。下一篇10-3会讲完整的备份执行与恢复导入那是本实例的操作核心。动手练习运行 App 进入备份页点「导出 JSON」然后用 DevEco Studio 的 Device File Explorer 找到沙箱目录下的 backup_xxx.json双击打开查看导出的内容结构。十、备份记录表字段设计详解四个字段撑起一次备份的档案backup_log 记录的不是备份内容而是「一次备份的档案」。逐字段拆解设计意图字段存什么为什么这么设计使用场景file_namebackup_1752xxx.json时间戳命名天然唯一无需 UUID列表展示、删除时定位文件source_tablebackup_source记录「这次备份的是哪张表」多表备份时区分日志归属size1248字节文件大小列表展示「占多大空间」排序、容量感知backup_time1752xxxms时间戳而非字符串可直接排序比较按时间倒序展示历史两个设计取舍1. 不落库 file_path。备份文件路径可由filesDir / file_name推导存了反而冗余——目录变了会留下脏数据。可推导的字段不落库是反范式设计的一条实用原则。2. backup_time 用 INTEGER 不用 TEXT。2025-07-01 10:00字符串比较需要格式化一致才能排序时间戳数字天然可比页面展示时再formatDate(ts)转字符串。存储用机器格式展示用人话格式。备份名的可读性扩展若希望文件名更友好可改为backup_20250701_1000.json时间戳格式化拼接字段设计不变只是命名规则变。十一、多表备份数据源范围的设计与遍历当前实例只备份 backup_source 一张表但字段source_table已为多表预留。多表备份的设计方案 A备份配置表——维护一张backup_scope表登记「可备份的表清单」字段名类型说明table_nameTEXT表名backup_source / memo / diary…order_noINTEGER备份顺序enabledINTEGER是否启用方案 B代码内置表清单——用一个静态数组声明staticreadonlyBACKUP_SCOPE:string[][backup_source,memo,diary];多表遍历备份的核心循环for(consttableofBackupDao.BACKUP_SCOPE){constrowsawaitBackupDao.queryTable(context,table);// 按表名查询constjsonJSON.stringify(rows);constfilebackup_${Date.now()}_${table}.json;// 每表一个文件awaitBackupDao.writeFile(context,file,json);awaitBackupDao.insertLog(context,{fileName:file,sourceTable:table});}每张表一个文件 一条日志source_table字段此时真正发挥「区分来源」的作用——日志列表能看出「哪张表、何时、多大」。十二、备份文件的内容结构JSON/CSV 的序列化契约序列化不只是把行拼成字符串更要在文件里写清楚「这是谁的数据」。JSON 结构带上表名与版本{version:1,table:backup_source,exportedAt:1752000000000,count:12,rows:[{id:1,name:华为 Mate 60 Pro,category:数码,amount:6999}]}version 字段的价值未来表结构加字段如新增 price旧备份恢复时靠 version 决定兼容策略——文件自带版本号恢复才有升级空间。CSV 的契约则简单首行表头即结构定义。表头是 CSV 的「元数据」——列名、列序都在第一行解析时按表头映射字段恢复就不怕列顺序变化。十三、恢复流程设计预览恢复是备份的逆过程数据层设计上分三步读文件readSync读出 JSON 文本对称于第七节的 writeSync清空数据源表DELETE FROM backup_source保证恢复后是「干净的快照」而非新旧混杂事务批量插入BEGIN→ 逐行 insert →COMMIT任一失败ROLLBACK回滚——恢复的原子性靠事务兜底。// 恢复伪代码三步走consttextawaitBackupDao.readFile(context,filePath);// ① 读constparsedJSON.parse(text);conststoreawaitBackupDao.getStore(context);store.beginTransaction();// ② 清 ③ 插事务内awaitstore.executeSql(DELETE FROM${BackupDao.SOURCE_TABLE});for(constrowofparsed.rows){awaitBackupDao.insertSource(store,row);}store.commit();事务是恢复的「后悔药」——中途失败整体回滚数据源表保持原样不会出现「删了一半、插了一半」的中间态。完整实现见 10-3。十四、FAQQ1备份日志为什么不存内容内容在沙箱文件里backup_log 只存索引文件名、大小、时间。数据库负责「查得快」文件负责「存得下」——两者职责分离。Q2file_name 用时间戳命名会冲突吗同一毫秒内连续两次备份理论上会重名但用户手动操作不可能在 1ms 内点两次导出实际可忽略若追求极端安全可在文件名后追加随机数。Q3恢复时为什么必须清空旧数据恢复语义是「把备份时点的状态还原回来」不清空会导致新旧数据叠加重复条目。备份是快照恢复就是整体替换不是合并。Q4JSON 和 CSV 该怎么选JSON 给程序恢复用结构完整、可含类型信息CSV 给人看用Excel 直接打开。生产环境可同时导出两种格式日志 type 字段区分。Q5表结构改了旧备份还能恢复吗能恢复但可能缺列。方案备份文件带 version 字段恢复时按版本做字段映射缺的列填默认值——这是「文件版本化」设计解决 schema 演进问题的思路。
返回列表