Asynchronous 颤振:如何从资产中异步读取文件,而不阻塞UI

Asynchronous 颤振:如何从资产中异步读取文件,而不阻塞UI,asynchronous,file-io,dart,flutter,Asynchronous,File Io,Dart,Flutter,在flatter中,rootBundle.load()给我一个ByteData对象 dart中的ByteData对象到底是什么?它可以用来异步读取文件吗 我真的不明白这背后的动机 为什么不给我一个好的ol'文件对象,或者更好的是资产的完整路径 在我的例子中,我希望以异步方式逐字节读取资产文件中的字节,然后写入新文件。(构建一个不会挂断UI的XOR解密工具) 这是我所能做的最好的了,它很糟糕地挂断了用户界面 loadEncryptedPdf(fileName, secretKey, cacheDi

在flatter中,
rootBundle.load()
给我一个
ByteData
对象

dart中的
ByteData
对象到底是什么?它可以用来异步读取文件吗

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

为什么不给我一个好的ol'
文件
对象,或者更好的是资产的完整路径

在我的例子中,我希望以异步方式逐字节读取资产文件中的字节,然后写入新文件。(构建一个不会挂断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;
}
loadEncryptedPdf(文件名、secretKey、cacheDir)异步{
最终lenSecretKey=secretKey.length;
final encryptedByteData=wait rootBundle.load('assets/$fileName');
最终输出文件路径=cacheDir+'/'+文件名;
最终输出文件=文件(输出文件路径);
如果(!await outputFile.exists()){
流解密程序()异步*{
//从encryptedByteData读取位,并流式传输异或反转位
对于(var索引=0;索引
在Dart中,字节数据与Java
字节缓冲类似。它包装一个字节数组,为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;
}
Future loadEncryptedPdf(
字符串文件名、字符串secretKey、字符串cacheDir)异步{
最终lenSecretKey=secretKey.length;
final encryptedByteData=wait rootBundle.load('assets/$fileName');
字符串路径=cacheDir+'/'+文件名;
最终输出文件=文件(路径);
如果(!await outputFile.exists()){
打印(“使用$secretKey..解密$fileName”);
Uint8List bytes=encryptedByteData.buffer.asUint8List();
for(int i=0;i
如果您所做的工作非常昂贵,并且您不想阻塞UI,请使用
包中的方法:flatter/foundation.dart
。这将在单独的隔离中运行提供的函数,并将结果异步返回给您
loadEncryptedPdf
必须是顶级函数或静态函数才能在此处使用,并且只能传递一个参数(但可以将它们放在映射中)

导入“包:flift/foundation.dart”;
Future loadEncryptedPdf(映射参数)异步{
//这在另一个隔离上运行
...
} 
最终字符串结果=等待计算(loadEncryptedPdf,{'fileName':/*…/*});

因此,尽管@Jonah Williams和@Richard Heap发布的答案本身并不足够,但我尝试了两者的结合,这对我很有好处

这里有一个完整的解决方案-

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

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;
}
导入“包:flift/foundation.dart”;
导入“package:path_provider/path_provider.dart”;
//保存临时缓存目录的未来
最终的未来=
(()async=>(await(await getTemporaryDirectory()).createTemp()).path)();
_XorderCryptIsolate(args){
Uint8List pdfBytes=args[0]。buffer.asUint8List();
字符串secretKey=args[1];
File outputFile=args[2];
int lenSecretKey=secretKey.length;
对于(int i=0;i0){
打印(“使用$secretKey..解密$assetFileName”);
等待计算(_xorDecryptIsolate,[pdfBytesData,secretKey,outputFile]);
打印('done!');
}
返回outputFilePath;
}

如何获取资产中文件的文件路径?我试图查找
getApplicationDocumentsDirectory()
,但它不在那里。甚至尝试将它放入
android/app/src/main/assets/
,并使用
文件
对象和安卓
file:///android_asset/
url,但这只是说文件不存在。。看起来我唯一的选择是现在就编写本机代码..请不要使用outputFile.exists()。更多信息:。这会导致
异常:只能从主隔离发送平台消息
无法使用隔离从资产读取文件:(
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;
}