在这里插入图片描述

一、功能概述

喝水记录应用虽然是本地应用,但很多用户会在更换设备或重装系统时希望"带走自己的喝水历史"。因此,一个可靠的"数据导出/导入"能力非常重要。本篇文章围绕"导入导出"模块展开,介绍如何在 Cordova Web 层 将 IndexedDB 中的记录序列化为 JSON 文件,并通过文件选择器导入外部 JSON;同时结合 OpenHarmony ArkTS 插件,在原生侧提供文件保存目录选择或导出结果提示等能力。

和前面的文章一样,我们将采用"一段代码一段说明"的结构,通过 HTML/JavaScript 和 ArkTS 示例,构建一条完整的数据迁移动线。

二、Web 端导出/导入界面结构

<div id="data-transfer-page" class="page page-data-transfer">
  <h1>数据导入导出</h1>

  <div class="transfer-actions">
    <button id="btn-export" class="btn-primary">导出数据为 JSON</button>
    <button id="btn-import" class="btn-secondary">从 JSON 导入数据</button>
  </div>

  <p id="transfer-status" class="text-muted"></p>
</div>

这段 HTML 定义了数据迁移页面的基本结构。两个按钮分别负责触发导出和导入操作,transfer-status 用于展示操作结果或错误信息。界面设计刻意保持简洁,让用户一眼就能理解两个核心操作:将当前数据导出为 JSON 文件,或从已有 JSON 文件中恢复数据。

.page-data-transfer {
  padding: 16px 24px;
}

.transfer-actions {
  display: flex;
  gap: 12px;
  margin-bottom: 12px;
}

CSS 为页面添加内边距,并使用 flex 布局将两个按钮横向排列,适当增加间距,避免误触。状态文本 transfer-status 可以通过全局样式中的 .text-muted 控制颜色和字号,与应用中其他提示文案保持一致。

三、导出:从 IndexedDB 收集数据并生成 JSON

async function exportData() {
  const records = await db.getAllDrinkRecords();
  const types = await db.getAllDrinkTypes();
  const containers = await db.getAllContainers();

  const payload = {
    version: 1,
    exportedAt: new Date().toISOString(),
    records,
    types,
    containers,
  };

  const json = JSON.stringify(payload, null, 2);
  triggerDownload(json, 'water-tracker-backup.json');
  updateTransferStatus('导出完成:已生成 JSON 文件');
}

exportData 函数负责从 IndexedDB 中收集需要迁移的全部数据,并打包为一个 JSON 对象。这里示例性地导出三张表:喝水记录 records、类型 types 和容器 containers,并额外保存版本号和导出时间,便于以后做兼容性处理。通过 JSON.stringify 将对象转换为字符串后,调用 triggerDownload 生成一个可下载的文件,文件名设为 water-tracker-backup.json。最后调用 updateTransferStatus 在页面上更新导出状态.

function triggerDownload(content, filename) {
  const blob = new Blob([content], { type: 'application/json' });
  const url = URL.createObjectURL(blob);

  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();

  URL.revokeObjectURL(url);
}

triggerDownload 使用浏览器原生的 Blob 和 URL API 创建一个临时的下载链接,并模拟点击行为触发下载。通过这种方式,用户可以直接在浏览器中保存一个包含所有关键数据的 JSON 文件,无需依赖额外插件。在 HarmonyOS Cordova 环境中,ArkWeb 也支持类似的下载行为,只是具体存储路径会由系统文件管理器来决定。

function updateTransferStatus(message) {
  const p = document.getElementById('transfer-status');
  if (!p) return;
  p.textContent = message;
}

updateTransferStatus 是一个小型辅助函数,用于将最近一次导入/导出操作的状态文案展示在页面上。这样用户在点击按钮后可以清楚知道当前操作是否成功,以及接下来需要做什么(例如"请保存下载的文件")。

四、导入:通过文件选择器读取 JSON 并写入数据库

function bindImportButton() {
  const btnImport = document.getElementById('btn-import');
  if (!btnImport) return;

  btnImport.addEventListener('click', () => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'application/json';

    input.addEventListener('change', () => {
      if (!input.files || input.files.length === 0) return;
      const file = input.files[0];
      readImportFile(file);
    });

    input.click();
  });
}

bindImportButton 为"从 JSON 导入数据"按钮绑定点击事件。点击时动态创建一个隐藏的 <input type="file"> 元素,并限制文件类型为 JSON。当用户选择文件后,触发 change 事件,从 input.files 中取出第一个文件,交给 readImportFile 进行解析。通过这种方式,我们可以完全基于 Web API 完成文件选择流程,与底层平台(包括 HarmonyOS)无关。

function readImportFile(file) {
  const reader = new FileReader();

  reader.onload = async () => {
    try {
      const text = reader.result as string;
      const payload = JSON.parse(text);
      await applyImportedData(payload);
      updateTransferStatus('导入完成:数据已写入本地数据库');
    } catch (err) {
      console.error('[Import] parse or apply failed', err);
      updateTransferStatus('导入失败:文件格式不正确或数据损坏');
    }
  };

  reader.readAsText(file);
}

readImportFile 使用 FileReader API 读取用户选择的 JSON 文件。在 onload 回调中,先通过 JSON.parse 解析文件内容为对象,然后调用 applyImportedData 将数据写入 IndexedDB。如果解析或写入过程中出现错误,会捕获异常并向用户展示友好的错误提示。通过这种错误处理,用户不会因为导入失败而丢失现有数据。

async function applyImportedData(payload) {
  if (!payload.records || !Array.isArray(payload.records)) {
    throw new Error('Invalid payload: missing records array');
  }

  for (const record of payload.records) {
    await db.addDrinkRecord(record);
  }

  if (payload.types && Array.isArray(payload.types)) {
    for (const type of payload.types) {
      await db.addDrinkType(type);
    }
  }
}

applyImportedData 负责将导入的数据逐条写入 IndexedDB。首先验证 payload 中是否包含有效的 records 数组,如果不存在则抛出错误。随后遍历每条记录并调用 db.addDrinkRecord 写入数据库;如果还有类型数据,也一并导入。通过这种逐条写入的方式,可以更精细地控制导入过程,并在某条数据有问题时及时发现。

document.addEventListener('DOMContentLoaded', () => {
  document.getElementById('btn-export')?.addEventListener('click', exportData);
  bindImportButton();
});

DOMContentLoaded 事件中绑定导出按钮和导入按钮的事件处理器,确保页面加载完成后用户可以立即使用这两个功能。

五、通过 Cordova 通知原生层导出完成

function syncExportStatusToNative(success, filename) {
  if (!window.cordova) {
    console.warn('[DataTransfer] cordova not ready, skip native sync');
    return;
  }

  cordova.exec(
    () => {
      console.info('[DataTransfer] sync export status success');
    },
    (err) => {
      console.error('[DataTransfer] sync export status failed', err);
    },
    'WaterTrackerDataTransfer',
    'onExportComplete',
    [{ success, filename }]
  );
}

syncExportStatusToNative 在导出完成后通知 ArkTS 插件。通过传递 success 标志和文件名,原生侧可以决定是否展示一个"导出成功"的通知,或者提供一个"打开文件所在目录"的快捷操作。这种反向通知机制让原生层能够参与到 Web 的数据迁移流程中,提升整体用户体验。

六、OpenHarmony ArkTS 插件与数据迁移

// entry/src/main/ets/plugins/WaterTrackerDataTransferPlugin.ets
import common from '@ohos.app.ability.common';

export interface ExportStatus {
  success: boolean;
  filename: string;
}

export class DataTransferStore {
  private static _lastExportStatus: ExportStatus | null = null;

  static setLastExportStatus(status: ExportStatus) {
    this._lastExportStatus = status;
  }

  static get lastExportStatus() {
    return this._lastExportStatus;
  }
}

export default class WaterTrackerDataTransferPlugin {
  context: common.UIAbilityContext;

  constructor(ctx: common.UIAbilityContext) {
    this.context = ctx;
  }

  onExportComplete(args: Array<Object>, callbackId: number) {
    const status = args[0] as ExportStatus;
    DataTransferStore.setLastExportStatus(status);
    console.info(`[DataTransferPlugin] export ${status.success ? 'success' : 'failed'}`);
  }
}

ArkTS 侧的 WaterTrackerDataTransferPlugin 插件接收来自 Web 的导出状态,并通过 DataTransferStore 缓存最新结果。这样 ArkUI 组件可以随时读取导出状态,并据此展示相应的提示或操作按钮。

七、ArkUI 中展示导出结果

// entry/src/main/ets/pages/DataTransferPage.ets
import { DataTransferStore } from '../plugins/WaterTrackerDataTransferPlugin';

@Component
struct DataTransferView {
  build() {
    const status = DataTransferStore.lastExportStatus;

    Column() {
      Text('数据迁移状态')
        .fontSize(18)
        .margin({ bottom: 8 });

      if (status) {
        Text(`上次导出:${status.success ? '成功' : '失败'}`)
          .fontSize(14);

        if (status.success) {
          Text(`文件名:${status.filename}`)
            .fontSize(14)
            .margin({ top: 4 });
        }
      } else {
        Text('暂无导出记录')
          .fontSize(14);
      }
    }
    .padding(16)
  }
}

DataTransferView 组件在原生界面中展示最近一次导出的状态。如果导出成功,会显示文件名;如果失败,则显示相应提示。这样用户在原生页面也能了解到数据迁移的进度和结果。

八、小结

本篇文章从导出表单、JSON 生成、文件下载到导入选择、文件解析、数据写入,再到 Cordova 桥接和 ArkTS 插件,完整演示了"导入导出"在 Cordova&openharmony 混合应用中的实现路径。Web 层通过 exportDatatriggerDownload 实现了数据导出,通过 bindImportButtonreadImportFile 实现了数据导入;syncExportStatusToNative 则将导出结果推送给原生侧,ArkTS 侧通过 DataTransferStoreWaterTrackerDataTransferPlugin 缓存状态,ArkUI 组件 DataTransferView 则提供原生展示入口。

通过"一段代码一段说明"的方式,我们把整个数据迁移流程拆解得足够细致。你可以在此基础上进一步扩展,例如添加数据验证、版本兼容性处理、加密导出等功能,让"导入导出"真正成为用户在设备迁移或数据备份时的可靠助手.

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐