Unverified Commit c0129046 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[framework] load files through ImmutableBuffer.fromFilePath (if exact file type) (#112892)

Update FileImage to use the new ImmutableBuffer.fromFilePath constructor to avoid loading bytes through the Dart heap. As a side effect, tis fixes #112881 which prevents dart:io from opening a file larger than INT_MAX bytes on windows.

For files that are not exactly a dart:io file, we still use the old method of calling loadBytes() in order to not make this a breaking change.
parent 049ea561
......@@ -991,17 +991,23 @@ class FileImage extends ImageProvider<FileImage> {
Future<ui.Codec> _loadAsync(FileImage key, DecoderBufferCallback? decode, DecoderCallback? decodeDeprecated) async {
assert(key == this);
final Uint8List bytes = await file.readAsBytes();
if (bytes.lengthInBytes == 0) {
// TODO(jonahwilliams): making this sync caused test failures that seem to
// indicate that we can fail to call evict unless at least one await has
// occurred in the test.
// https://github.com/flutter/flutter/issues/113044
final int lengthInBytes = await file.length();
if (lengthInBytes == 0) {
// The file may become available later.
PaintingBinding.instance.imageCache.evict(key);
throw StateError('$file is empty and cannot be loaded as an image.');
}
if (decode != null) {
return decode(await ui.ImmutableBuffer.fromUint8List(bytes));
if (file.runtimeType == File) {
return decode(await ui.ImmutableBuffer.fromFilePath(file.path));
}
return decode(await ui.ImmutableBuffer.fromUint8List(await file.readAsBytes()));
}
return decodeDeprecated!(bytes);
return decodeDeprecated!(await file.readAsBytes());
}
@override
......
......@@ -136,6 +136,29 @@ void main() {
expect(completer.debugLabel, 'MemoryImage(${describeIdentity(bytes)}) - Resized(40×40)');
});
test('File image throws error when given a real but non-image file', () async {
final Completer<Exception> error = Completer<Exception>();
FlutterError.onError = (FlutterErrorDetails details) {
error.complete(details.exception as Exception);
};
final FileImage provider = FileImage(File('pubspec.yaml'));
expect(imageCache.statusForKey(provider).untracked, true);
expect(imageCache.pendingImageCount, 0);
provider.resolve(ImageConfiguration.empty);
expect(imageCache.statusForKey(provider).pending, true);
expect(imageCache.pendingImageCount, 1);
expect(await error.future, isException
.having((Exception exception) => exception.toString(), 'toString', contains('Invalid image data')));
// Invalid images are marked as pending so that we do not attempt to reload them.
expect(imageCache.statusForKey(provider).untracked, false);
expect(imageCache.pendingImageCount, 1);
}, skip: kIsWeb); // [intended] The web cannot load files.
}
class FakeCodec implements Codec {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment