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

Dev*_*wal 5 file-io asynchronous dart flutter

在颤动中,rootBundle.load()给了我一个ByteData对象。

到底什么是ByteData?可以用来异步读取文件吗?

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

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

就我而言,我想从资产文件中异步读取字节,逐字节并写入新文件。(构建一个不会挂起 UI 的 XOR 解密东西)

这是我能做的最好的事情,但它却严重挂起了用户界面。

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;
}
Run Code Online (Sandbox Code Playgroud)

Ric*_*eap 5

在 Dart 中 aByteData类似于 JavaByteBuffer。它包装了一个字节数组,为 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;
}
Run Code Online (Sandbox Code Playgroud)


Dev*_*wal 2

因此,虽然@Jonah Williams 和@Richard Heap 发布的答案本身还不够,但我尝试了两者的结合,这对我来说很有用。

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

使用path_provider包获取缓存目录

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;
}
Run Code Online (Sandbox Code Playgroud)