dart 大文件读取
文章目录
好久没水文章了, 强行水一篇
dart 中不可避免会出现文件读取的情况, 甚至是很大的文件, 比如 200M 的文件
如果一次性读入内存,虽然也行得通, 但是如果在 flutter 中开启个 200M 大小的字节数组, 一不小心可能就 crash 了, 这时候就需要使用大文件读取的方案
异步读取
核心方法:
1file.openRead();
这个方法可以指定开始和结束的坐标, 并开启一个 stream
stream
回调信息是 List<int>
,单次最大读取 65536
个字节
示例
1class FileUtils {
2 File file;
3
4 FileUtils(this.file);
5
6 // 读取文件的某个范围返回
7 Future<List<int>> getRange(int start, int end) async {
8 if (file == null || !file.existsSync()) {
9 throw FileNotExistsError();
10 }
11 if (start < 0) {
12 throw RangeError.range(start, 0, file.lengthSync());
13 }
14 if (end > file.lengthSync()) {
15 throw RangeError.range(end, 0, file.lengthSync());
16 }
17
18 final c = MyCompleter<List<int>>();
19
20 List<int> result = [];
21 file.openRead(start, end).listen((data) {
22 result.addAll(data);
23 }).onDone(() {
24 c.reply(result);
25 });
26
27 return c.future;
28 }
29}
用到的 completer
在这里
1import 'dart:async';
2
3class MyCompleter<T> {
4 Completer<T> completer = Completer();
5
6 MyCompleter();
7
8 Future<T> get future => completer.future;
9
10 void reply(T result) {
11 if (!completer.isCompleted) {
12 completer.complete(result);
13 }
14 }
15}
这里主要是封装了一个指定某个范围返回的方法, 比如我写了一个简单的 dart 包, 通过读取元数据的方案, 来读取图片大小, 这样不用通过完整的解码即可完成, 效率比 dart:ui.Image 解码后查看宽高要高的多
项目地址: https://github.com/CaiJingLong/dart_image_size_getter
比如判断一个文件是不是 jpeg 格式, 需要判断开头两个字节和结尾两个字节是不是 0xFF 0xD9 开头, 0xFF 0xD8 结尾
原始代码是这样的, 很不清晰, 也比较难以阅读:
经过修改后是这样的
1 static Future<bool> isJpg(File file) async {
2 if (file == null || !file.existsSync()) {
3 return false;
4 }
5
6 const start = [0xFF, 0xD9];
7 const end = [0xFF, 0xD8];
8
9 final completer = MyCompleter<bool>();
10
11 void foo() async {
12 final util = FileUtils(file);
13 final length = file.lengthSync();
14
15 final startList = await util.getRange(0, 2);
16 final endList = await util.getRange(length - 2, length);
17
18 const eq = ListEquality();
19
20 completer.reply(eq.equals(start, startList) && eq.equals(end, endList));
21 }
22
23 foo();
24
25 return completer.future;
26 }
同步读取
当然有的时候,你在 flutter 的 build 方法中操作文件,那就不适合用异步了,因为太多的 FutureBuilder
也是很烦的,这个时候可以用同步方法。
1 List<int> getRangeSync(File file, int start, int end) {
2 final accessFile = file.openSync();
3 try {
4 accessFile.setPositionSync(start);
5 return accessFile.readSync(end - start).toList();
6 } finally {
7 accessFile.closeSync();
8 }
9 }
手动 close 是一个好习惯
关于 readInfoSync
这个方法我在实际用的时候会出现问题,具体原因不太清楚
后记
项目地址: https://github.com/CaiJingLong/dart_image_size_getter
1pub get
2
3dart bin/main.dart
以上