代码之家  ›  专栏  ›  技术社区  ›  Dev Aggarwal

flutter:如何在不阻塞ui的情况下异步地从资产中读取文件

  •  1
  • Dev Aggarwal  · 技术社区  · 7 年前

    在颤振中, rootBundle.load() 给我一个 ByteData 目标

    到底是什么 Bytedata公司 飞镖中的物体?它可以用来异步读取文件吗?

    我真的不明白这背后的动机。

    为什么不给我一个好的 File 对象,或者更好的是资产的完整路径?

    在我的例子中,我想异步地从一个资产文件读取字节,逐字节读取并写入一个新文件。(构建一个不挂断用户界面的异或解密项)

    这是我所能做的最好的一件事,它很不幸地挂断了用户界面。

    loadEncryptedPdf(fileName, secretKey, cacheDir) async {
      final lenSecretKey = secretKey.length;
    
      final encryptedByteData = await rootBundle.load('assets/$fileName');
    
      final outputFilePath = cacheDir + '/' + fileName;
      final outputFile = File(outputFilePath);
    
      if (!await outputFile.exists()) {
        Stream decrypter() async* {
          // read bits from encryptedByteData, and stream the xor inverted bits
    
          for (var index = 0; index < encryptedByteData.lengthInBytes; index++)
            yield encryptedByteData.getUint8(index) ^
            secretKey.codeUnitAt(index % lenSecretKey);
          print('done!');
        }
    
        print('decrypting $fileName using $secretKey ..');
        await outputFile.openWrite(encoding: AsciiCodec()).addStream(decrypter());
        print('finished');
      }
    
      return outputFilePath;
    }
    
    3 回复  |  直到 7 年前
        1
  •  2
  •   Richard Heap    7 年前

    在飞镖A中 ByteData 类似于Java ByteBuffer 是的。它包装一个字节数组,为1、2和4字节整数(都是尾数)提供getter和setter函数。

    因为您想操作字节,所以只需在底层字节数组(DART)上工作是最容易的 Uint8List )中。 RootBundle.load() 已经将整个资产读入内存,所以在内存中更改它并将其写出来。

    Future<String> loadEncryptedPdf(
        String fileName, String secretKey, String cacheDir) async {
      final lenSecretKey = secretKey.length;
    
      final encryptedByteData = await rootBundle.load('assets/$fileName');
    
      String path = cacheDir + '/' + fileName;
      final outputFile = File(path);
    
      if (!await outputFile.exists()) {
        print('decrypting $fileName using $secretKey ..');
    
        Uint8List bytes = encryptedByteData.buffer.asUint8List();
        for (int i = 0; i < bytes.length; i++) {
          bytes[i] ^= secretKey.codeUnitAt(i % lenSecretKey);
        }
    
        await outputFile.writeAsBytes(bytes);
        print('finished');
      }
    
      return path;
    }
    
        2
  •  2
  •   Jonah Williams    7 年前

    如果您所做的工作非常昂贵,并且不想阻塞ui,请使用 compute 方法来自 package:flutter/foundation.dart 是的。这将在单独的隔离中运行提供的函数,并异步返回结果。 loadEncryptedPdf 必须是顶级或静态函数才能在此处使用它,并且您只能传递一个参数(但您可以将它们放在映射中)。

    import 'package:flutter/foundation.dart';
    
    Future<String> loadEncryptedPdf(Map<String, String> arguments) async {
      // this runs on another isolate
      ...
    } 
    
    final String result = await compute(loadEncryptedPdf, {'fileName': /*.../*});
    
        3
  •  2
  •   Dev Aggarwal    7 年前

    所以,虽然jonah williams和richard heap自己发布的答案不够,但我尝试了两者的结合,这对我很好。

    这是一个完整的解决方案-

    使用路径提供程序包获取缓存目录

    import 'package:flutter/foundation.dart';
    import 'package:path_provider/path_provider.dart';
    
    // Holds a Future to a temporary cache directory
    final cacheDirFuture =
        (() async => (await (await getTemporaryDirectory()).createTemp()).path)();
    
    _xorDecryptIsolate(args) {
      Uint8List pdfBytes = args[0].buffer.asUint8List();
      String secretKey = args[1];
      File outputFile = args[2];
    
      int lenSecretKey = secretKey.length;
    
      for (int i = 0; i < pdfBytes.length; i++)
        pdfBytes[i] ^= secretKey.codeUnitAt(i % lenSecretKey);
    
      outputFile.writeAsBytesSync(pdfBytes, flush: true);
    
    }
    
    
    /// decrypt a file from assets using XOR,
    /// and return the path to a cached-temporary decrypted file.
    xorDecryptFromAssets(String assetFileName, String secretKey) async {
      final pdfBytesData = await rootBundle.load('assets/$assetFileName');
      final outputFilePath = (await pdfCacheDirFuture) + '/' + assetFileName;
      final outputFile = File(outputFilePath);
    
      if ((await outputFile.stat()).size > 0) {
        print('decrypting $assetFileName using $secretKey ..');
        await compute(_xorDecryptIsolate, [pdfBytesData, secretKey, outputFile]);
        print('done!');
      }
    
      return outputFilePath;
    }