Flutter dart 将 hive 保存的数据导出到文件以便稍后检索

Flutter dart export hive saved data to file to retrieve later

我正在开发条形码应用程序并将数据保存到配置单元。我需要知道的是有一种方法可以将保存的配置单元数据库导出到备份文件,并且能够在应用程序崩溃或您的 phone 丢失时检索它。这是为了盲人可访问性。想要将数据导出到一个文件,我可以将其保存到我的电脑上进行存储,如果发生什么情况,我不必再次扫描所有产品来构建数据库。如果 hive 不能做到这一点,有人可以指出 flutter dart 数据库可以做到这一点的方向。谢谢

好的,答案对我不起作用。这是我的模型文件的副本

    import 'package:hive/hive.dart';

    part 'product.g.dart';
    @HiveType(typeId: 0)
    class Product extends HiveObject{
      @HiveField(0)
      String itemName;
      @HiveField(1)
      String barCode;
      @HiveField(2)
      String bcType;

      Product(this.itemName, this.barCode, this.bcType);
    }

然后我这样称呼我的盒子 var box = Hive.box('products');

如何将其编码为 json 以便保存?

我用下一个

    Future<File> _createBackupFile() async {
      /// This example uses the OS temp directory

文件备份File = File('${Directory.systemTemp.path}/backup_barcode.json');

      try {
        /// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
    var barcodeBox = Hive.box<Product>('products');
       backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));

        return backupFile;
      } catch (e) {
        // TODO: handle exception
    print(e);
      }
    }

据我所知,没有“开箱即用”的解决方案。这在很大程度上取决于您想要如何做到这一点的用例(因为有很多方法)。有关我如何为我的应用程序执行此操作的完整示例,您可以在此处查看: https://github.com/Kounex/obs_blade/blob/master/lib/views/settings/logs/log_detail/log_detail.dart (I made use of the share 打包以便轻松导出 - 但这不是必需的)

Flutter 也有自己的文件读写文档(https://flutter.dev/docs/cookbook/persistence/reading-writing-files) - 我将补充一些信息来总结一下:

存储位置


首先我们要考虑把“备份文件”存放在哪里。 Flutter 自己公开了您可以使用的公共路径(此外,path_provider 包为您提供了更大的灵活性)。如果你想要这个备份文件是临时的,你可以例如使用:

Directory.systemTemp;

文档指出:“这是操作系统提供的用于创建临时文件和目录的目录。” OS 将确保删除它们在不同的场合,所以你不必担心。您还可以在此临时目录中创建其他目录以使其更易于区分,例如:

Directory.systemTemp.createTemp('my_app');

重要提示: 这适用于非敏感数据。如果您正在处理的任何内容包含敏感数据(如姓名、地址等),您必须确保数据安全/数据隐私。在这种情况下,我会使用前面提到的 path_provider 包,并在文档目录 (getApplicationDocumentsDirectory()) 中创建这些文件,并确保在使用/导出后立即删除它们。即使加密内容也可能是个好主意 - 但我不会在这里深入探讨。

文件管理


一旦我们知道文件的存储位置,我们只需要创建它们。前面的 flutter 文档的第 3 章和第 4 章确切地说明了如何做到这一点,所以我更关注 what 来写。

一种常用且非常方便的数据组合方式是 JSON。 Flutter 也有相应的文档:https://flutter.dev/docs/development/data-and-backend/json

由于您使用的是 Hive,您可能已经有 classes 表示您的框中的条目,您可以轻松地添加 toJson() 函数,其中 return a Map<String, dynamic>(如文档中所示),您可以使用它最终将所需信息写入文件。

基于您的 Hive class,以下是如何在 otder 中调整它以正确序列化它:

import 'package:hive/hive.dart';

part 'product.g.dart';

@HiveType(typeId: 0)
class Product extends HiveObject{
  @HiveField(0)
  String itemName;

  @HiveField(1)
  String barCode;

  @HiveField(2)
  String bcType;

  Product(this.itemName, this.barCode, this.bcType);

  /// This function will automatically be used by the [jsonEncode()] function internally
  Map<String, dynamic> toJson() => {
    'itemName': this.itemName,
    'barCode': this.barCode,
    'bcType': this.bcType,
  }
}

一个小示例实现可能如下所示:

Future<File?> _createBackupFile() async {
  /// This example uses the OS temp directory
  File backupFile = File('${Directory.systemTemp.path}/backup_barcode.json');

  try {
    /// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
    backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));

    return backupFile;
  } catch (e) {
    // TODO: handle exception
  }
}

这会将您的 Hive 框的 JSON 表示保存在临时 OS 目录中。您可以将目录替换为最适合您的目录(在 Android 上,例如在外部存储上,以便于访问)。

现在你必须考虑如何以及何时触发它。例如,您可以通过触发按钮来手动执行此操作,也可以在执行特定操作(如添加新条形码)后自动执行此操作,然后选择一种适合您的方式来访问文件。如前所述,将文件保存在易于访问的地方,例如 Android 上的外部存储或使用共享包是可能的解决方案。

Android 清单应包含这些:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
        android:requestLegacyExternalStorage="true"

您需要 this package and this 个软件包才能继续。

现在提供一种将数据备份到所需位置的方法:

Future<void> createBackup() async {
if (Hive.box<Product>('products').isEmpty) {
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('No Products Stored.')),
  );
  return;
}
ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(content: Text('Creating backup...')),
);
Map<String, dynamic> map = Hive.box<Product>('products')
    .toMap()
    .map((key, value) => MapEntry(key.toString(), value));
String json = jsonEncode(map);
await Permission.storage.request();
Directory dir = await _getDirectory();
String formattedDate = DateTime.now()
    .toString()
    .replaceAll('.', '-')
    .replaceAll(' ', '-')
    .replaceAll(':', '-');
String path = '${dir.path}$formattedDate.json';//Change .json to your desired file format(like .barbackup or .hive).
File backupFile = File(path);
await backupFile.writeAsString(json);
ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(content: Text('Backup saved in folder Barcodes')),
);}


Future<Directory> _getDirectory() async {
    const String pathExt = 'Barcodes/';//This is the name of the folder where the backup is stored
    Directory newDirectory = Directory('/storage/emulated/0/' + pathExt);//Change this to any desired location where the folder will be created
    if (await newDirectory.exists() == false) {
      return newDirectory.create(recursive: true);
    }
    return newDirectory;
  }

最后,使用按钮调用此函数,它将以当前时间作为名称以 JSON 格式保存备份。

createBackup()

在此之后将数据恢复回 Hive,

Future<void> restoreBackup() async {
ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(content: Text('Restoring backup...')),
);
FilePickerResult? file = await FilePicker.platform.pickFiles(
  type: FileType.any,
);
if (file != null) {
  File files = File(file.files.single.path.toString());
  Hive.box<Product>('products').clear();
  Map<String, dynamic> map = jsonDecode(await files.readAsString());
  for (var i = 0; i < map.length; i++) {
    Product product = Product.fromJson(i.toString(), map);
    Hive.box<Product>('products').add(product);
  }
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('Restored Successfully...')),
  );
}
}

最后,使用按钮调用此函数,它会打开文件选择器,您可以在其中 select 备份文件,它会删除现有数据并循环添加备份中的每个项目。

restoreBackup()