Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: How to read file from assets, asynchronously, without blocking the UI

In flutter, rootBundle.load() gives me a ByteData object.

What exactly is a ByteData object in dart? Can It be used to read files asynchronously?

I don't really understand the motive behind this.

Why not just give me a good ol' File object, or better yet the full path of the asset?

In my case, I want to read bytes from an asset file asynchronously, byte by byte and write to a new file. (to build an XOR decryption thingy that doesn't hang up the UI)

This is the best I could do, and it miserably hangs up the 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;
}
like image 350
Dev Aggarwal Avatar asked Jan 03 '23 03:01

Dev Aggarwal


1 Answers

In Dart a ByteData is similar to a Java ByteBuffer. It wraps a byte array, providing getter and setter functions for 1, 2 and 4 byte integers (both endians).

Since you want to manipulate bytes it's easiest to just work on the underlying byte array (a Dart Uint8List). RootBundle.load() will have already read the whole asset into memory, so change it in memory and write it out.

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;
}
like image 135
Richard Heap Avatar answered Feb 09 '23 00:02

Richard Heap