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



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

飞镖中的ByteData对象究竟是什么?它可以用来异步读取文件吗?

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

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

在我的情况下,我希望从资产文件中异步地逐字节读取字节,然后写入新文件。(构建一个不会挂起UI的XOR解密工具(

这是我所能做的最好的事情,它悲惨地挂断了UI。

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;
}

在Dart中,ByteData类似于JavaByteBuffer。它封装了一个字节数组,为1、2和4字节整数(均为endians(提供getter和setter函数。

由于您想要操作字节,所以只处理底层字节数组(DartUint8List(是最简单的。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;
}

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

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': /*.../*});

所以,虽然@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;
}

最新更新