数据备份与恢复:HarmonyOS APP数据保护

📌 核心要点:数据备份与恢复是应用数据安全的最后一道防线,HarmonyOS提供了本地备份、云端备份和分布式备份三种机制,确保数据万无一失

一、背景与动机

数据丢了,这事儿可大可小。轻则用户设置没了,重则几年积累的照片、聊天记录全没了。你肯定听过那种"手机坏了,数据全丢"的悲惨故事吧?

作为开发者,我们有责任帮用户守护好他们的数据。HarmonyOS在这方面做得挺全面——本地备份、云端备份、分布式备份,三重保障。而且备份恢复机制对应用透明,你只需要声明哪些数据需要备份,剩下的系统自动搞定。

说白了,备份恢复就是给数据买保险。平时感觉不到它的存在,但真出事儿的时候,你会发现这笔"保费"交得太值了。

二、核心原理

2.1 备份架构

HarmonyOS的备份恢复体系分三个层次:

graph TD
    A[应用数据]:::primary --> B{备份触发}
    B -->|自动触发| C[定时备份]
    B -->|手动触发| D[用户备份]
    B -->|系统触发| E[升级备份]
    
    C --> F[备份管理器]
    D --> F
    E --> F
    
    F --> G{备份策略}
    G -->|本地备份| H[本地存储]
    G -->|云端备份| I[云端存储]
    G -->|分布式备份| J[对端设备]
    
    H --> K[备份文件]
    I --> L[云端数据]
    J --> M[分布式数据]
    
    K --> N[恢复时读取]
    L --> N
    M --> N
    
    N --> O[数据恢复]
    O --> A
    
    classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
    classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
    classDef info fill:#2196F3,stroke:#1976D2,color:#fff

2.2 备份类型对比

备份类型存储位置触发时机适用场景恢复速度
本地备份设备存储应用升级、定时快速恢复、版本回退极快
云端备份华为云定时、用户触发跨设备恢复、灾难恢复中等
分布式备份对端设备设备连接时多设备协同

2.3 备份流程

完整的备份流程包含以下步骤:

  1. 数据收集:系统扫描需要备份的数据目录
  2. 数据打包:将数据序列化为备份文件
  3. 数据加密:可选的数据加密保护
  4. 数据传输:传输到备份目标位置
  5. 元数据记录:记录备份时间、版本等信息
  6. 清理旧备份:根据策略清理过期备份

2.4 恢复流程

恢复流程与备份相反:

  1. 备份定位:根据恢复请求找到对应备份
  2. 完整性校验:验证备份数据完整性
  3. 数据解密:如果备份加密则解密
  4. 数据解包:解析备份文件
  5. 数据写入:恢复到应用数据目录
  6. 应用通知:通知应用数据已恢复

三、代码实战

3.1 基础用法:配置备份规则

最简单的备份——在配置文件中声明需要备份的数据。

3.1.1 配置backup_config.json
{
  "backupConfig": {
    "version": "1.0",
    "description": "应用数据备份配置",
    
    // 需要备份的数据目录
    "includeDirs": [
      {
        "path": "/data/storage/el2/base/files/",
        "description": "用户文件数据",
        "priority": "high"
      },
      {
        "path": "/data/storage/el2/database/",
        "description": "数据库文件",
        "priority": "high"
      },
      {
        "path": "/data/storage/el2/base/preferences/",
        "description": "用户偏好设置",
        "priority": "medium"
      }
    ],
    
    // 排除的文件或目录
    "excludePatterns": [
      "*.tmp",
      "*.log",
      "cache/*"
    ],
    
    // 备份策略
    "backupStrategy": {
      "autoBackup": true,              // 启用自动备份
      "backupInterval": 86400000,      // 备份间隔:24小时
      "maxBackupCount": 5,             // 最多保留5个备份
      "enableCloudBackup": true,       // 启用云端备份
      "enableEncryption": true         // 启用加密
    },
    
    // 恢复策略
    "restoreStrategy": {
      "conflictResolution": "latest",  // 冲突解决:使用最新数据
      "preserveUserData": true         // 保留用户数据
    }
  }
}
3.1.2 在module.json5中引用配置
{
  "module": {
    "abilities": [
      {
        "name": "MainAbility",
        "metadata": [
          {
            "name": "ohos.backup.config",
            "resource": "$profile:backup_config"
          }
        ]
      }
    ]
  }
}

3.2 进阶用法:自定义备份恢复逻辑

有些场景下,默认的备份机制不够灵活,需要自定义备份恢复逻辑。

import backupExtension from '@ohos.application.BackupExtension';
import fs from '@ohos.file.fs';
import relationalStore from '@ohos.data.relationalStore';

// 自定义备份Extension
export default class CustomBackupExtension extends backupExtension.BackupExtension {
  
  // 备份数据
  async onBackup(): Promise<void> {
    console.info('[备份] 开始备份数据');
    
    try {
      // 1. 备份数据库数据
      await this.backupDatabase();
      
      // 2. 备份用户文件
      await this.backupUserFiles();
      
      // 3. 备份应用设置
      await this.backupPreferences();
      
      // 4. 生成备份清单
      await this.generateBackupManifest();
      
      console.info('[备份] 备份完成');
      
    } catch (error) {
      console.error(`[备份] 失败: ${JSON.stringify(error)}`);
      throw error;
    }
  }
  
  // 恢复数据
  async onRestore(restoreType: backupExtension.RestoreType): Promise<void> {
    console.info(`[恢复] 开始恢复数据,类型: ${restoreType}`);
    
    try {
      // 1. 验证备份完整性
      const isValid = await this.verifyBackupIntegrity();
      if (!isValid) {
        throw new Error('备份数据损坏');
      }
      
      // 2. 恢复数据库数据
      await this.restoreDatabase();
      
      // 3. 恢复用户文件
      await this.restoreUserFiles();
      
      // 4. 恢复应用设置
      await this.restorePreferences();
      
      // 5. 数据迁移(如果版本不同)
      await this.migrateDataIfNeeded();
      
      console.info('[恢复] 恢复完成');
      
    } catch (error) {
      console.error(`[恢复] 失败: ${JSON.stringify(error)}`);
      throw error;
    }
  }
  
  // 备份数据库
  private async backupDatabase(): Promise<void> {
    const dbPath = '/data/storage/el2/database/MyApp.db';
    const backupPath = '/data/storage/el2/base/files/backup/database_backup.db';
    
    // 检查数据库是否存在
    if (!fs.accessSync(dbPath)) {
      console.info('[备份] 数据库不存在,跳过');
      return;
    }
    
    // 复制数据库文件
    fs.copyFileSync(dbPath, backupPath);
    
    // 导出关键数据为JSON(可选)
    const jsonData = await this.exportDatabaseToJson();
    const jsonPath = '/data/storage/el2/base/files/backup/database_export.json';
    
    const file = fs.openSync(jsonPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
    fs.writeSync(file.fd, jsonData);
    fs.closeSync(file);
    
    console.info('[备份] 数据库备份完成');
  }
  
  // 导出数据库为JSON
  private async exportDatabaseToJson(): Promise<string> {
    try {
      const config: relationalStore.StoreConfig = {
        name: 'MyApp.db',
        securityLevel: relationalStore.SecurityLevel.S1
      };
      
      const rdbStore = await relationalStore.getRdbStore(this.context, config);
      
      // 导出关键表数据
      const tables = ['users', 'notes', 'settings'];
      const exportData: Record<string, any[]> = {};
      
      for (const table of tables) {
        const predicates = new relationalStore.RdbPredicates(table);
        const resultSet = await rdbStore.query(predicates);
        
        exportData[table] = [];
        while (resultSet.goToNextRow()) {
          const row: Record<string, any> = {};
          for (let i = 0; i < resultSet.columnCount; i++) {
            const columnName = resultSet.getColumnName(i);
            row[columnName] = this.getColumnValue(resultSet, i);
          }
          exportData[table].push(row);
        }
        resultSet.close();
      }
      
      return JSON.stringify(exportData, null, 2);
      
    } catch (error) {
      console.error(`[导出] 失败: ${JSON.stringify(error)}`);
      return '{}';
    }
  }
  
  // 备份用户文件
  private async backupUserFiles(): Promise<void> {
    const userFilesDir = '/data/storage/el2/base/files/user_data';
    const backupDir = '/data/storage/el2/base/files/backup/user_files';
    
    // 创建备份目录
    if (!fs.accessSync(backupDir)) {
      fs.mkdirSync(backupDir, true);
    }
    
    // 递归复制文件
    await this.copyDirectory(userFilesDir, backupDir);
    
    console.info('[备份] 用户文件备份完成');
  }
  
  // 备份应用设置
  private async backupPreferences(): Promise<void> {
    const prefsPath = '/data/storage/el2/base/preferences/main_preferences.xml';
    const backupPath = '/data/storage/el2/base/files/backup/preferences_backup.xml';
    
    if (fs.accessSync(prefsPath)) {
      fs.copyFileSync(prefsPath, backupPath);
      console.info('[备份] 设置备份完成');
    }
  }
  
  // 生成备份清单
  private async generateBackupManifest(): Promise<void> {
    const manifest = {
      version: '1.0',
      timestamp: Date.now(),
      appVersion: this.getAppVersion(),
      items: [
        { name: 'database', path: 'backup/database_backup.db', size: this.getFileSize('backup/database_backup.db') },
        { name: 'user_files', path: 'backup/user_files', type: 'directory' },
        { name: 'preferences', path: 'backup/preferences_backup.xml', size: this.getFileSize('backup/preferences_backup.xml') }
      ],
      checksum: await this.calculateChecksum()
    };
    
    const manifestPath = '/data/storage/el2/base/files/backup/manifest.json';
    const file = fs.openSync(manifestPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
    fs.writeSync(file.fd, JSON.stringify(manifest, null, 2));
    fs.closeSync(file);
    
    console.info('[备份] 清单生成完成');
  }
  
  // 恢复数据库
  private async restoreDatabase(): Promise<void> {
    const backupPath = '/data/storage/el2/base/files/backup/database_backup.db';
    const dbPath = '/data/storage/el2/database/MyApp.db';
    
    if (!fs.accessSync(backupPath)) {
      console.warn('[恢复] 数据库备份不存在,跳过');
      return;
    }
    
    // 关闭现有数据库连接
    await relationalStore.deleteRdbStore(this.context, 'MyApp.db');
    
    // 恢复数据库文件
    fs.copyFileSync(backupPath, dbPath);
    
    console.info('[恢复] 数据库恢复完成');
  }
  
  // 恢复用户文件
  private async restoreUserFiles(): Promise<void> {
    const backupDir = '/data/storage/el2/base/files/backup/user_files';
    const userFilesDir = '/data/storage/el2/base/files/user_data';
    
    if (!fs.accessSync(backupDir)) {
      console.warn('[恢复] 用户文件备份不存在,跳过');
      return;
    }
    
    // 清空现有文件
    if (fs.accessSync(userFilesDir)) {
      fs.rmdirSync(userFilesDir, true);
    }
    
    // 恢复文件
    await this.copyDirectory(backupDir, userFilesDir);
    
    console.info('[恢复] 用户文件恢复完成');
  }
  
  // 恢复应用设置
  private async restorePreferences(): Promise<void> {
    const backupPath = '/data/storage/el2/base/files/backup/preferences_backup.xml';
    const prefsPath = '/data/storage/el2/base/preferences/main_preferences.xml';
    
    if (fs.accessSync(backupPath)) {
      fs.copyFileSync(backupPath, prefsPath);
      console.info('[恢复] 设置恢复完成');
    }
  }
  
  // 验证备份完整性
  private async verifyBackupIntegrity(): Promise<boolean> {
    const manifestPath = '/data/storage/el2/base/files/backup/manifest.json';
    
    if (!fs.accessSync(manifestPath)) {
      console.error('[验证] 备份清单不存在');
      return false;
    }
    
    try {
      const file = fs.openSync(manifestPath, fs.OpenMode.READ_ONLY);
      const stat = fs.statSync(manifestPath);
      const buffer = new ArrayBuffer(stat.size);
      fs.readSync(file.fd, buffer);
      fs.closeSync(file);
      
      const manifest = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(buffer) as any));
      
      // 验证校验和
      const currentChecksum = await this.calculateChecksum();
      if (manifest.checksum !== currentChecksum) {
        console.error('[验证] 校验和不匹配');
        return false;
      }
      
      // 验证备份文件是否存在
      for (const item of manifest.items) {
        const itemPath = `/data/storage/el2/base/files/${item.path}`;
        if (!fs.accessSync(itemPath)) {
          console.error(`[验证] 备份项不存在: ${item.name}`);
          return false;
        }
      }
      
      return true;
      
    } catch (error) {
      console.error(`[验证] 失败: ${JSON.stringify(error)}`);
      return false;
    }
  }
  
  // 数据迁移(版本不同时)
  private async migrateDataIfNeeded(): Promise<void> {
    const manifestPath = '/data/storage/el2/base/files/backup/manifest.json';
    const file = fs.openSync(manifestPath, fs.OpenMode.READ_ONLY);
    const stat = fs.statSync(manifestPath);
    const buffer = new ArrayBuffer(stat.size);
    fs.readSync(file.fd, buffer);
    fs.closeSync(file);
    
    const manifest = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(buffer) as any));
    const currentVersion = this.getAppVersion();
    
    if (manifest.appVersion !== currentVersion) {
      console.info(`[迁移] 版本不同,执行迁移: ${manifest.appVersion} -> ${currentVersion}`);
      await this.executeMigration(manifest.appVersion, currentVersion);
    }
  }
  
  // 执行数据迁移
  private async executeMigration(fromVersion: string, toVersion: string): Promise<void> {
    // 根据版本差异执行迁移逻辑
    // 例如:表结构变更、数据格式转换等
    console.info(`[迁移] 从 ${fromVersion} 迁移到 ${toVersion}`);
  }
  
  // 辅助方法
  private async copyDirectory(src: string, dest: string): Promise<void> {
    // 实现目录复制逻辑
    if (!fs.accessSync(dest)) {
      fs.mkdirSync(dest, true);
    }
    
    const files = fs.listFileSync(src);
    for (const file of files) {
      const srcPath = `${src}/${file}`;
      const destPath = `${dest}/${file}`;
      
      const stat = fs.statSync(srcPath);
      if (stat.isDirectory()) {
        await this.copyDirectory(srcPath, destPath);
      } else {
        fs.copyFileSync(srcPath, destPath);
      }
    }
  }
  
  private getColumnValue(resultSet: relationalStore.ResultSet, index: number): any {
    const type = resultSet.getColumnType(index);
    switch (type) {
      case relationalStore.ColumnType.TYPE_INTEGER:
        return resultSet.getLong(index);
      case relationalStore.ColumnType.TYPE_FLOAT:
        return resultSet.getDouble(index);
      case relationalStore.ColumnType.TYPE_STRING:
        return resultSet.getString(index);
      case relationalStore.ColumnType.TYPE_BLOB:
        return resultSet.getBlob(index);
      case relationalStore.ColumnType.TYPE_NULL:
        return null;
      default:
        return null;
    }
  }
  
  private getAppVersion(): string {
    // 从应用配置获取版本号
    return '1.0.0';
  }
  
  private getFileSize(path: string): number {
    const fullPath = `/data/storage/el2/base/files/${path}`;
    if (fs.accessSync(fullPath)) {
      const stat = fs.statSync(fullPath);
      return stat.size;
    }
    return 0;
  }
  
  private async calculateChecksum(): Promise<string> {
    // 计算备份文件的校验和
    return 'checksum_' + Date.now();
  }
}

3.3 完整示例:带云端备份的完整方案

来个完整的实战案例——支持本地和云端备份的完整方案。

import backupExtension from '@ohos.application.BackupExtension';
import cloudBackup from '@ohos.cloud.backup';
import fs from '@ohos.file.fs';
import relationalStore from '@ohos.data.relationalStore';

// 完整的备份恢复Extension
export default class FullBackupExtension extends backupExtension.BackupExtension {
  private cloudBackupClient: cloudBackup.CloudBackupClient | null = null;
  
  async onCreate(): Promise<void> {
    // 初始化云端备份客户端
    this.cloudBackupClient = cloudBackup.createCloudBackupClient({
      appId: 'com.example.myapp',
      cloudPath: '/backup/myapp/'
    });
    
    console.info('[备份服务] 初始化完成');
  }
  
  // 执行备份
  async onBackup(): Promise<void> {
    console.info('[备份] 开始执行备份');
    
    const backupId = `backup_${Date.now()}`;
    const backupDir = `/data/storage/el2/base/files/backups/${backupId}`;
    
    try {
      // 创建备份目录
      fs.mkdirSync(backupDir, true);
      
      // 1. 本地备份
      await this.performLocalBackup(backupDir);
      
      // 2. 云端备份(异步)
      this.performCloudBackup(backupDir, backupId).then(() => {
        console.info('[云端备份] 完成');
      }).catch((error) => {
        console.error(`[云端备份] 失败: ${JSON.stringify(error)}`);
      });
      
      // 3. 清理旧备份
      await this.cleanupOldBackups();
      
      console.info(`[备份] 完成,ID: ${backupId}`);
      
    } catch (error) {
      console.error(`[备份] 失败: ${JSON.stringify(error)}`);
      // 清理失败的备份
      if (fs.accessSync(backupDir)) {
        fs.rmdirSync(backupDir, true);
      }
      throw error;
    }
  }
  
  // 本地备份
  private async performLocalBackup(backupDir: string): Promise<void> {
    // 备份数据库
    await this.backupDatabase(backupDir);
    
    // 备份文件
    await this.backupFiles(backupDir);
    
    // 备份设置
    await this.backupSettings(backupDir);
    
    // 生成备份元数据
    await this.generateMetadata(backupDir);
  }
  
  // 备份数据库(增量备份)
  private async backupDatabase(backupDir: string): Promise<void> {
    const dbBackupPath = `${backupDir}/database`;
    fs.mkdirSync(dbBackupPath, true);
    
    // 获取数据库列表
    const dbDir = '/data/storage/el2/database';
    const dbFiles = fs.listFileSync(dbDir);
    
    for (const dbFile of dbFiles) {
      if (!dbFile.endsWith('.db')) continue;
      
      const srcPath = `${dbDir}/${dbFile}`;
      const destPath = `${dbBackupPath}/${dbFile}`;
      
      // 检查是否需要增量备份
      const lastBackupPath = await this.getLastBackupPath(dbFile);
      if (lastBackupPath && await this.isIncrementalBackupNeeded(srcPath, lastBackupPath)) {
        // 增量备份
        await this.incrementalBackup(srcPath, lastBackupPath, destPath);
      } else {
        // 全量备份
        fs.copyFileSync(srcPath, destPath);
      }
      
      console.info(`[数据库备份] ${dbFile} 完成`);
    }
  }
  
  // 增量备份
  private async incrementalBackup(srcPath: string, lastBackupPath: string, destPath: string): Promise<void> {
    // 获取上次备份时间
    const lastStat = fs.statSync(lastBackupPath);
    const lastBackupTime = lastStat.mtime;
    
    // 导出增量数据
    const config: relationalStore.StoreConfig = {
      name: srcPath.split('/').pop()!,
      securityLevel: relationalStore.SecurityLevel.S1
    };
    
    const rdbStore = await relationalStore.getRdbStore(this.context, config);
    
    // 查询修改时间大于上次备份时间的数据
    const predicates = new relationalStore.RdbPredicates('data_table');
    predicates.greaterThan('updated_time', lastBackupTime);
    
    const resultSet = await rdbStore.query(predicates);
    
    // 将增量数据写入备份文件
    const incrementalData: any[] = [];
    while (resultSet.goToNextRow()) {
      const row: Record<string, any> = {};
      for (let i = 0; i < resultSet.columnCount; i++) {
        row[resultSet.getColumnName(i)] = this.getColumnValue(resultSet, i);
      }
      incrementalData.push(row);
    }
    resultSet.close();
    
    // 写入增量备份文件
    const file = fs.openSync(destPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
    fs.writeSync(file.fd, JSON.stringify({
      type: 'incremental',
      baseBackup: lastBackupPath,
      timestamp: Date.now(),
      data: incrementalData
    }));
    fs.closeSync(file);
    
    console.info(`[增量备份] ${incrementalData.length} 条记录`);
  }
  
  // 备份用户文件
  private async backupFiles(backupDir: string): Promise<void> {
    const filesBackupPath = `${backupDir}/files`;
    fs.mkdirSync(filesBackupPath, true);
    
    const userFilesDir = '/data/storage/el2/base/files/user_data';
    
    if (!fs.accessSync(userFilesDir)) {
      console.info('[文件备份] 用户文件目录不存在,跳过');
      return;
    }
    
    // 压缩文件(节省空间)
    await this.compressDirectory(userFilesDir, `${filesBackupPath}/user_data.zip`);
    
    console.info('[文件备份] 完成');
  }
  
  // 备份设置
  private async backupSettings(backupDir: string): Promise<void> {
    const settingsBackupPath = `${backupDir}/settings`;
    fs.mkdirSync(settingsBackupPath, true);
    
    const prefsDir = '/data/storage/el2/base/preferences';
    
    if (fs.accessSync(prefsDir)) {
      const prefsFiles = fs.listFileSync(prefsDir);
      
      for (const prefsFile of prefsFiles) {
        const srcPath = `${prefsDir}/${prefsFile}`;
        const destPath = `${settingsBackupPath}/${prefsFile}`;
        fs.copyFileSync(srcPath, destPath);
      }
    }
    
    console.info('[设置备份] 完成');
  }
  
  // 生成备份元数据
  private async generateMetadata(backupDir: string): Promise<void> {
    const metadata = {
      version: '2.0',
      timestamp: Date.now(),
      appVersion: this.getAppVersion(),
      device: this.getDeviceInfo(),
      size: await this.calculateBackupSize(backupDir),
      checksum: await this.calculateChecksum(backupDir),
      components: {
        database: fs.accessSync(`${backupDir}/database`),
        files: fs.accessSync(`${backupDir}/files`),
        settings: fs.accessSync(`${backupDir}/settings`)
      }
    };
    
    const metadataPath = `${backupDir}/metadata.json`;
    const file = fs.openSync(metadataPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
    fs.writeSync(file.fd, JSON.stringify(metadata, null, 2));
    fs.closeSync(file);
  }
  
  // 云端备份
  private async performCloudBackup(backupDir: string, backupId: string): Promise<void> {
    if (!this.cloudBackupClient) {
      console.warn('[云端备份] 客户端未初始化');
      return;
    }
    
    // 检查网络状态
    const networkAvailable = await this.checkNetworkAvailable();
    if (!networkAvailable) {
      console.warn('[云端备份] 网络不可用,跳过');
      return;
    }
    
    // 上传备份文件
    const metadataPath = `${backupDir}/metadata.json`;
    const metadata = JSON.parse(fs.readTextSync(metadataPath));
    
    // 加密备份(可选)
    const encryptedBackupPath = await this.encryptBackup(backupDir);
    
    // 上传到云端
    await this.cloudBackupClient.upload({
      localPath: encryptedBackupPath || backupDir,
      cloudPath: `/${backupId}/`,
      metadata: metadata,
      onProgress: (progress: number) => {
        console.info(`[云端备份] 上传进度: ${progress}%`);
      }
    });
    
    // 更新云端备份列表
    await this.updateCloudBackupList(backupId, metadata);
  }
  
  // 执行恢复
  async onRestore(restoreType: backupExtension.RestoreType): Promise<void> {
    console.info(`[恢复] 开始恢复,类型: ${restoreType}`);
    
    let backupPath: string;
    
    switch (restoreType) {
      case backupExtension.RestoreType.LOCAL:
        backupPath = await this.selectLocalBackup();
        break;
        
      case backupExtension.RestoreType.CLOUD:
        backupPath = await this.downloadCloudBackup();
        break;
        
      case backupExtension.RestoreType.DISTRIBUTED:
        backupPath = await this.getDistributedBackup();
        break;
        
      default:
        throw new Error(`不支持的恢复类型: ${restoreType}`);
    }
    
    if (!backupPath || !fs.accessSync(backupPath)) {
      throw new Error('备份数据不存在');
    }
    
    // 验证备份
    const isValid = await this.verifyBackup(backupPath);
    if (!isValid) {
      throw new Error('备份数据损坏');
    }
    
    // 执行恢复
    await this.executeRestore(backupPath);
    
    console.info('[恢复] 完成');
  }
  
  // 选择本地备份
  private async selectLocalBackup(): Promise<string> {
    const backupsDir = '/data/storage/el2/base/files/backups';
    
    if (!fs.accessSync(backupsDir)) {
      throw new Error('没有可用的本地备份');
    }
    
    // 获取所有备份
    const backups = fs.listFileSync(backupsDir)
      .filter(name => name.startsWith('backup_'))
      .sort((a, b) => b.localeCompare(a));  // 按时间倒序
    
    if (backups.length === 0) {
      throw new Error('没有可用的本地备份');
    }
    
    // 返回最新的备份
    return `${backupsDir}/${backups[0]}`;
  }
  
  // 下载云端备份
  private async downloadCloudBackup(): Promise<string> {
    if (!this.cloudBackupClient) {
      throw new Error('云端备份客户端未初始化');
    }
    
    // 获取云端备份列表
    const cloudBackups = await this.cloudBackupClient.listBackups();
    
    if (cloudBackups.length === 0) {
      throw new Error('没有可用的云端备份');
    }
    
    // 选择最新的备份
    const latestBackup = cloudBackups.sort((a, b) => b.timestamp - a.timestamp)[0];
    
    // 下载备份
    const downloadPath = `/data/storage/el2/base/files/temp/cloud_backup_${Date.now()}`;
    fs.mkdirSync(downloadPath, true);
    
    await this.cloudBackupClient.download({
      cloudPath: latestBackup.path,
      localPath: downloadPath,
      onProgress: (progress: number) => {
        console.info(`[云端恢复] 下载进度: ${progress}%`);
      }
    });
    
    // 解密备份(如果加密了)
    const decryptedPath = await this.decryptBackup(downloadPath);
    
    return decryptedPath || downloadPath;
  }
  
  // 执行恢复
  private async executeRestore(backupPath: string): Promise<void> {
    // 读取元数据
    const metadataPath = `${backupPath}/metadata.json`;
    const metadata = JSON.parse(fs.readTextSync(metadataPath));
    
    // 恢复数据库
    if (metadata.components.database) {
      await this.restoreDatabase(`${backupPath}/database`);
    }
    
    // 恢复文件
    if (metadata.components.files) {
      await this.restoreFiles(`${backupPath}/files`);
    }
    
    // 恢复设置
    if (metadata.components.settings) {
      await this.restoreSettings(`${backupPath}/settings`);
    }
    
    // 数据迁移(如果版本不同)
    if (metadata.appVersion !== this.getAppVersion()) {
      await this.migrateData(metadata.appVersion, this.getAppVersion());
    }
  }
  
  // 清理旧备份
  private async cleanupOldBackups(): Promise<void> {
    const backupsDir = '/data/storage/el2/base/files/backups';
    const maxBackups = 5;  // 最多保留5个备份
    
    if (!fs.accessSync(backupsDir)) return;
    
    const backups = fs.listFileSync(backupsDir)
      .filter(name => name.startsWith('backup_'))
      .sort((a, b) => b.localeCompare(a));
    
    // 删除超出数量的旧备份
    for (let i = maxBackups; i < backups.length; i++) {
      const oldBackupPath = `${backupsDir}/${backups[i]}`;
      fs.rmdirSync(oldBackupPath, true);
      console.info(`[清理] 删除旧备份: ${backups[i]}`);
    }
  }
  
  // 辅助方法(简化实现)
  private async getLastBackupPath(dbFile: string): Promise<string | null> {
    // 查找该数据库的上次备份路径
    return null;
  }
  
  private async isIncrementalBackupNeeded(srcPath: string, lastBackupPath: string): Promise<boolean> {
    // 判断是否需要增量备份
    return false;
  }
  
  private async compressDirectory(src: string, dest: string): Promise<void> {
    // 压缩目录(简化实现)
    fs.copyFileSync(src, dest);
  }
  
  private async calculateBackupSize(backupDir: string): Promise<number> {
    // 计算备份大小
    return 0;
  }
  
  private async calculateChecksum(backupDir: string): Promise<string> {
    return 'checksum_' + Date.now();
  }
  
  private getAppVersion(): string {
    return '1.0.0';
  }
  
  private getDeviceInfo(): any {
    return { model: 'unknown', os: 'HarmonyOS' };
  }
  
  private async checkNetworkAvailable(): Promise<boolean> {
    return true;
  }
  
  private async encryptBackup(backupDir: string): Promise<string | null> {
    return null;
  }
  
  private async decryptBackup(backupDir: string): Promise<string | null> {
    return null;
  }
  
  private async updateCloudBackupList(backupId: string, metadata: any): Promise<void> {
    // 更新云端备份列表
  }
  
  private async getDistributedBackup(): Promise<string> {
    throw new Error('分布式备份暂不支持');
  }
  
  private async verifyBackup(backupPath: string): Promise<boolean> {
    return true;
  }
  
  private async restoreDatabase(dbBackupPath: string): Promise<void> {
    // 恢复数据库
  }
  
  private async restoreFiles(filesBackupPath: string): Promise<void> {
    // 恢复文件
  }
  
  private async restoreSettings(settingsBackupPath: string): Promise<void> {
    // 恢复设置
  }
  
  private async migrateData(fromVersion: string, toVersion: string): Promise<void> {
    // 数据迁移
  }
  
  private getColumnValue(resultSet: relationalStore.ResultSet, index: number): any {
    const type = resultSet.getColumnType(index);
    switch (type) {
      case relationalStore.ColumnType.TYPE_INTEGER:
        return resultSet.getLong(index);
      case relationalStore.ColumnType.TYPE_FLOAT:
        return resultSet.getDouble(index);
      case relationalStore.ColumnType.TYPE_STRING:
        return resultSet.getString(index);
      case relationalStore.ColumnType.TYPE_BLOB:
        return resultSet.getBlob(index);
      default:
        return null;
    }
  }
  
  onDestroy(): void {
    console.info('[备份服务] 销毁');
  }
}

四、踩坑与注意事项

4.1 备份时机选择

坑点:在应用运行时备份,可能导致数据不一致。

解决方案:在应用暂停或退出时触发备份。

// 在Ability生命周期中触发备份
export default class MainAbility extends UIAbility {
  onPause(): void {
    // 应用暂停时触发备份
    this.triggerBackup();
  }
  
  onDestroy(): void {
    // 应用销毁时触发备份
    this.triggerBackup();
  }
  
  private triggerBackup(): void {
    // 发送备份请求
    backupExtension.requestBackup('com.example.myapp');
  }
}

4.2 备份文件过大

坑点:备份文件过大,导致存储空间不足或上传超时。

解决方案:增量备份+压缩+分片上传。

// 备份策略配置
const backupStrategy = {
  // 启用增量备份
  enableIncremental: true,
  
  // 压缩备份数据
  enableCompression: true,
  compressionLevel: 6,  // 压缩级别:1-9
  
  // 分片上传(大文件)
  chunkSize: 5 * 1024 * 1024,  // 每片5MB
  
  // 排除大文件
  excludeLargeFiles: true,
  maxFileSize: 50 * 1024 * 1024  // 超过50MB的文件不备份
};

4.3 恢复数据冲突

坑点:恢复数据时与现有数据冲突,导致数据覆盖或丢失。

解决方案:实现冲突解决策略。

// 冲突解决策略
enum ConflictResolution {
  KEEP_LOCAL = 'keep_local',      // 保留本地数据
  KEEP_BACKUP = 'keep_backup',    // 使用备份数据
  MERGE = 'merge',                // 合并数据
  ASK_USER = 'ask_user'           // 让用户选择
}

async function resolveConflict(localData: any, backupData: any, strategy: ConflictResolution): Promise<any> {
  switch (strategy) {
    case ConflictResolution.KEEP_LOCAL:
      return localData;
      
    case ConflictResolution.KEEP_BACKUP:
      return backupData;
      
    case ConflictResolution.MERGE:
      // 合并策略:时间戳优先
      if (localData.updatedTime > backupData.updatedTime) {
        return localData;
      } else {
        return backupData;
      }
      
    case ConflictResolution.ASK_USER:
      // 弹出对话框让用户选择
      return await showConflictDialog(localData, backupData);
  }
}

4.4 云端备份失败

坑点:网络不稳定导致云端备份失败,但本地备份也被清理。

解决方案:先完成本地备份,再异步上传云端。

async function performBackup(): Promise<void> {
  // 1. 本地备份(必须成功)
  const localBackupPath = await performLocalBackup();
  
  // 2. 云端备份(异步,失败不影响本地)
  performCloudBackup(localBackupPath).catch(error => {
    console.error(`[云端备份] 失败: ${JSON.stringify(error)}`);
    // 标记待上传,下次重试
    markPendingUpload(localBackupPath);
  });
}

4.5 备份加密安全

坑点:备份数据未加密,敏感信息泄露。

解决方案:启用备份加密。

// 加密备份配置
const encryptionConfig = {
  enabled: true,
  algorithm: 'AES-256-GCM',
  keyDerivation: 'PBKDF2',
  
  // 密钥来源
  keySource: 'user_password',  // 用户密码派生
  
  // 加密范围
  encryptDatabase: true,
  encryptFiles: true,
  encryptSettings: true
};

五、HarmonyOS 6适配说明

HarmonyOS 6在备份恢复方面带来了重要增强:

5.1 新增特性

  1. 智能备份:系统根据数据变更频率自动调整备份策略
  2. 增量备份优化:增量备份效率提升50%+
  3. 云端备份加速:支持断点续传和并行上传
  4. 备份验证增强:支持数据完整性自动校验

5.2 API变更

新增API

// 获取备份状态
const status = await backupExtension.getBackupStatus();

// 智能备份(系统自动判断备份时机)
await backupExtension.smartBackup();

// 增量备份查询
const changes = await backupExtension.getIncrementalChanges(sinceTime);

// 备份验证
const validationResult = await backupExtension.validateBackup(backupPath);

废弃API

  • forceBackup():改用smartBackup()

5.3 适配代码示例

import backupExtension from '@ohos.application.BackupExtension';

// HarmonyOS 6备份Extension
export default class HarmonyOS6BackupExtension extends backupExtension.BackupExtension {
  
  async onBackup(): Promise<void> {
    // 使用智能备份策略
    const backupStrategy = await this.determineBackupStrategy();
    
    console.info(`[智能备份] 策略: ${backupStrategy.type}`);
    
    switch (backupStrategy.type) {
      case 'full':
        await this.performFullBackup();
        break;
        
      case 'incremental':
        await this.performIncrementalBackup(backupStrategy.sinceTime);
        break;
        
      case 'delta':
        await this.performDeltaBackup(backupStrategy.changes);
        break;
    }
  }
  
  // 确定备份策略
  private async determineBackupStrategy(): Promise<BackupStrategy> {
    // 获取上次备份时间
    const lastBackupTime = await this.getLastBackupTime();
    const timeSinceLastBackup = Date.now() - lastBackupTime;
    
    // 获取数据变更统计
    const changeStats = await backupExtension.getIncrementalChanges(lastBackupTime);
    
    // 根据变更比例决定策略
    const changeRatio = changeStats.changedRecords / changeStats.totalRecords;
    
    if (timeSinceLastBackup > 7 * 24 * 60 * 60 * 1000) {
      // 超过7天,全量备份
      return { type: 'full' };
    } else if (changeRatio > 0.3) {
      // 变更超过30%,全量备份
      return { type: 'full' };
    } else if (changeRatio > 0.1) {
      // 变更10%-30%,增量备份
      return { type: 'incremental', sinceTime: lastBackupTime };
    } else {
      // 变更小于10%,差量备份
      return { type: 'delta', changes: changeStats.changedRecords };
    }
  }
  
  // 增量备份(HarmonyOS 6优化)
  private async performIncrementalBackup(sinceTime: number): Promise<void> {
    // 获取增量变更
    const changes = await backupExtension.getIncrementalChanges(sinceTime);
    
    // 按表分组
    const changesByTable = this.groupChangesByTable(changes);
    
    // 并行备份各表
    const backupPromises = Object.entries(changesByTable).map(([table, records]) => {
      return this.backupTableChanges(table, records);
    });
    
    await Promise.all(backupPromises);
    
    console.info(`[增量备份] ${changes.changedRecords} 条记录`);
  }
  
  // 差量备份(HarmonyOS 6新增)
  private async performDeltaBackup(changes: number): Promise<void> {
    // 只备份变更的字段,而非整条记录
    const deltaChanges = await this.getDeltaChanges();
    
    const backupPath = this.getBackupPath();
    const file = fs.openSync(backupPath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY);
    
    fs.writeSync(file.fd, JSON.stringify({
      type: 'delta',
      timestamp: Date.now(),
      deltas: deltaChanges
    }));
    
    fs.closeSync(file);
    
    console.info(`[差量备份] ${deltaChanges.length} 个字段变更`);
  }
  
  // 云端备份(HarmonyOS 6加速)
  private async performCloudBackup(localBackupPath: string): Promise<void> {
    const cloudClient = cloudBackup.createCloudBackupClient({
      appId: 'com.example.myapp',
      // HarmonyOS 6新增配置
      enableResume: true,        // 启用断点续传
      parallelUpload: 3,         // 并行上传数
      compressBeforeUpload: true // 上传前压缩
    });
    
    // 上传(支持断点续传)
    await cloudClient.upload({
      localPath: localBackupPath,
      cloudPath: `/backup/${Date.now()}/`,
      onProgress: (progress: number, speed: number) => {
        console.info(`[云端上传] 进度: ${progress}%, 速度: ${speed} KB/s`);
      },
      onResume: () => {
        console.info('[云端上传] 恢复中断的上传');
      }
    });
  }
  
  // 备份验证(HarmonyOS 6增强)
  async onRestore(restoreType: backupExtension.RestoreType): Promise<void> {
    const backupPath = await this.getBackupPath(restoreType);
    
    // 验证备份完整性
    const validationResult = await backupExtension.validateBackup(backupPath);
    
    if (!validationResult.isValid) {
      // 尝试修复
      const repairResult = await this.repairBackup(backupPath, validationResult.errors);
      
      if (!repairResult.success) {
        throw new Error('备份数据损坏且无法修复');
      }
    }
    
    // 执行恢复
    await this.executeRestore(backupPath);
  }
  
  // 辅助方法
  private async getLastBackupTime(): Promise<number> {
    return Date.now() - 24 * 60 * 60 * 1000;
  }
  
  private async performFullBackup(): Promise<void> {
    // 全量备份实现
  }
  
  private groupChangesByTable(changes: any): Record<string, any[]> {
    return {};
  }
  
  private async backupTableChanges(table: string, records: any[]): Promise<void> {
    // 备份表变更
  }
  
  private async getDeltaChanges(): Promise<any[]> {
    return [];
  }
  
  private getBackupPath(): string {
    return '';
  }
  
  private async getBackupPath(restoreType: backupExtension.RestoreType): Promise<string> {
    return '';
  }
  
  private async repairBackup(backupPath: string, errors: any[]): Promise<any> {
    return { success: false };
  }
  
  private async executeRestore(backupPath: string): Promise<void> {
    // 执行恢复
  }
}

interface BackupStrategy {
  type: 'full' | 'incremental' | 'delta';
  sinceTime?: number;
  changes?: number;
}

5.4 性能优化建议

HarmonyOS 6环境下,充分利用智能备份:

// 性能优化配置
const performanceConfig = {
  // 启用智能备份(推荐)
  enableSmartBackup: true,
  
  // 备份频率调整
  autoAdjustFrequency: true,
  minBackupInterval: 6 * 60 * 60 * 1000,   // 最小6小时
  maxBackupInterval: 48 * 60 * 60 * 1000,  // 最大48小时
  
  // 增量备份优化
  incrementalThreshold: 0.3,  // 变更超过30%触发全量备份
  
  // 云端备份优化
  cloudBackupConfig: {
    parallelUpload: 3,        // 3线程并行上传
    chunkSize: 10 * 1024 * 1024,  // 每片10MB
    enableResume: true        // 启用断点续传
  }
};

六、总结

维度评价
学习难度⭐⭐⭐
使用频率⭐⭐⭐
重要程度⭐⭐⭐⭐⭐
调试难度⭐⭐⭐⭐

核心收获

  1. 备份恢复是数据安全的最后一道防线,宁可备而不用,不可用而不备
  2. 三重备份机制各有优势:本地备份快、云端备份安全、分布式备份灵活
  3. 增量备份大幅减少备份开销,但要注意定期全量备份
  4. 冲突解决策略要提前设计,恢复时的数据冲突处理很关键
  5. HarmonyOS 6的智能备份是重大改进,系统自动优化备份策略,开发者更省心

下一步建议

  • 研究分布式场景下的跨设备备份恢复
  • 探索端云协同的备份策略
  • 实现用户可控的备份管理界面
  • 学习数据迁移和版本兼容处理

蓝胖子样样好
79 声望702 粉丝

Never give up,and you will be successful