Added withReadStream option to load a file in chunks (useful for large files)

This commit is contained in:
redsolver 2020-11-12 18:28:57 +01:00 committed by Miguel Ruivo
parent d2d4de7bf8
commit 2aafd1131c
6 changed files with 66 additions and 8 deletions

View File

@ -1,3 +1,6 @@
## 2.1.0
Adds `withReadStream` that allows bigger files to be streamed read into a `Stream<List<int>>`. Thanks @redsolver.
## 2.0.13 ## 2.0.13
Updates `extension` helper getter to use the `name` property instead of `path`, since the latest isn't available on the Web, hence, the extension wouldn't be as well. Thank you @markgrancapal. Updates `extension` helper getter to use the `name` property instead of `path`, since the latest isn't available on the Web, hence, the extension wouldn't be as well. Thank you @markgrancapal.

View File

@ -49,6 +49,9 @@ abstract class FilePicker extends PlatformInterface {
/// If [withData] is set, picked files will have its byte data immediately available on memory as [Uint8List] /// If [withData] is set, picked files will have its byte data immediately available on memory as [Uint8List]
/// which can be useful if you are picking it for server upload or similar. /// which can be useful if you are picking it for server upload or similar.
/// ///
/// If [withReadStream] is set, picked files will have its byte data available as a [Stream<List<int>>]
/// which can be useful for uploading and processing large files.
///
/// If you want to track picking status, for example, because some files may take some time to be /// If you want to track picking status, for example, because some files may take some time to be
/// cached (particularly those picked from cloud providers), you may want to set [onFileLoading] handler /// cached (particularly those picked from cloud providers), you may want to set [onFileLoading] handler
/// that will give you the current status of picking. /// that will give you the current status of picking.
@ -64,6 +67,7 @@ abstract class FilePicker extends PlatformInterface {
bool allowCompression, bool allowCompression,
bool allowMultiple = false, bool allowMultiple = false,
bool withData, bool withData,
bool withReadStream,
}) async => }) async =>
throw UnimplementedError('pickFiles() has not been implemented.'); throw UnimplementedError('pickFiles() has not been implemented.');

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:file_picker/src/platform_file.dart'; import 'package:file_picker/src/platform_file.dart';
@ -25,6 +26,7 @@ class FilePickerIO extends FilePicker {
bool allowCompression = true, bool allowCompression = true,
bool allowMultiple = false, bool allowMultiple = false,
bool withData = false, bool withData = false,
bool withReadStream = false,
}) => }) =>
_getPath( _getPath(
type, type,
@ -33,6 +35,7 @@ class FilePickerIO extends FilePicker {
allowedExtensions, allowedExtensions,
onFileLoading, onFileLoading,
withData, withData,
withReadStream,
); );
@override @override
@ -59,6 +62,7 @@ class FilePickerIO extends FilePicker {
List<String> allowedExtensions, List<String> allowedExtensions,
Function(FilePickerStatus) onFileLoading, Function(FilePickerStatus) onFileLoading,
bool withData, bool withData,
bool withReadStream,
) async { ) async {
final String type = describeEnum(fileType); final String type = describeEnum(fileType);
if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) { if (type != 'custom' && (allowedExtensions?.isNotEmpty ?? false)) {
@ -87,8 +91,20 @@ class FilePickerIO extends FilePicker {
return null; return null;
} }
return FilePickerResult( final List<PlatformFile> platformFiles = <PlatformFile>[];
result.map((file) => PlatformFile.fromMap(file)).toList());
for (final platformFileMap in result) {
platformFiles.add(
PlatformFile.fromMap(
platformFileMap,
readStream: withReadStream
? File(platformFileMap['path']).openRead()
: null,
),
);
}
return FilePickerResult(platformFiles);
} on PlatformException catch (e) { } on PlatformException catch (e) {
print('[$_tag] Platform exception: $e'); print('[$_tag] Platform exception: $e');
rethrow; rethrow;

View File

@ -12,6 +12,8 @@ class FilePickerWeb extends FilePicker {
Element _target; Element _target;
final String _kFilePickerInputsDomId = '__file_picker_web-file-input'; final String _kFilePickerInputsDomId = '__file_picker_web-file-input';
final int _readStreamChunkSize = 1000 * 1000; // 1 MB
static final FilePickerWeb platform = FilePickerWeb._(); static final FilePickerWeb platform = FilePickerWeb._();
FilePickerWeb._() { FilePickerWeb._() {
@ -43,6 +45,7 @@ class FilePickerWeb extends FilePicker {
Function(FilePickerStatus) onFileLoading, Function(FilePickerStatus) onFileLoading,
bool allowCompression, bool allowCompression,
bool withData = true, bool withData = true,
bool withReadStream = false,
}) async { }) async {
final Completer<List<PlatformFile>> filesCompleter = final Completer<List<PlatformFile>> filesCompleter =
Completer<List<PlatformFile>>(); Completer<List<PlatformFile>>();
@ -63,12 +66,18 @@ class FilePickerWeb extends FilePicker {
final List<File> files = uploadInput.files; final List<File> files = uploadInput.files;
final List<PlatformFile> pickedFiles = []; final List<PlatformFile> pickedFiles = [];
void addPickedFile(File file, Uint8List bytes, String path) { void addPickedFile(
File file,
Uint8List bytes,
String path,
Stream<List<int>> readStream,
) {
pickedFiles.add(PlatformFile( pickedFiles.add(PlatformFile(
name: file.name, name: file.name,
path: path, path: path,
size: bytes != null ? bytes.length ~/ 1024 : -1, size: bytes != null ? bytes.length ~/ 1024 : file.size,
bytes: bytes, bytes: bytes,
readStream: readStream,
)); ));
if (pickedFiles.length >= files.length) { if (pickedFiles.length >= files.length) {
@ -77,10 +86,15 @@ class FilePickerWeb extends FilePicker {
} }
files.forEach((File file) { files.forEach((File file) {
if (withReadStream) {
addPickedFile(file, null, null, _openFileReadStream(file));
return;
}
if (!withData) { if (!withData) {
final FileReader reader = FileReader(); final FileReader reader = FileReader();
reader.onLoadEnd.listen((e) { reader.onLoadEnd.listen((e) {
addPickedFile(file, null, reader.result); addPickedFile(file, null, reader.result, null);
}); });
reader.readAsDataUrl(file); reader.readAsDataUrl(file);
return; return;
@ -88,7 +102,7 @@ class FilePickerWeb extends FilePicker {
final FileReader reader = FileReader(); final FileReader reader = FileReader();
reader.onLoadEnd.listen((e) { reader.onLoadEnd.listen((e) {
addPickedFile(file, reader.result, null); addPickedFile(file, reader.result, null, null);
}); });
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
}); });
@ -129,4 +143,20 @@ class FilePickerWeb extends FilePicker {
} }
return ''; return '';
} }
Stream<List<int>> _openFileReadStream(File file) async* {
final reader = FileReader();
int start = 0;
while (start < file.size) {
final end = start + _readStreamChunkSize > file.size
? file.size
: start + _readStreamChunkSize;
final blob = file.slice(start, end);
reader.readAsArrayBuffer(blob);
await reader.onLoad.first;
yield reader.result;
start += _readStreamChunkSize;
}
}
} }

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
class PlatformFile { class PlatformFile {
@ -5,10 +6,11 @@ class PlatformFile {
this.path, this.path,
this.name, this.name,
this.bytes, this.bytes,
this.readStream,
this.size, this.size,
}); });
PlatformFile.fromMap(Map data) PlatformFile.fromMap(Map data, {this.readStream})
: this.path = data['path'], : this.path = data['path'],
this.name = data['name'], this.name = data['name'],
this.bytes = data['bytes'], this.bytes = data['bytes'],
@ -28,6 +30,9 @@ class PlatformFile {
/// or easily upload to somewhere else. /// or easily upload to somewhere else.
final Uint8List bytes; final Uint8List bytes;
/// File content as stream
final Stream<List<int>> readStream;
/// The file size in KB. /// The file size in KB.
final int size; final int size;

View File

@ -1,7 +1,7 @@
name: file_picker name: file_picker
description: A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support. description: A package that allows you to use a native file explorer to pick single or multiple absolute file paths, with extension filtering support.
homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker homepage: https://github.com/miguelpruivo/plugins_flutter_file_picker
version: 2.0.13 version: 2.1.0
dependencies: dependencies:
flutter: flutter: