-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v1.4.0 #16
v1.4.0 #16
Changes from all commits
f571796
6f43f7b
dedfa0e
72957fc
52a213c
19e6be3
6b49c83
5ce5a5a
870cdae
d844bfc
cf46631
b3a491b
41e1ad7
9a8d652
21f5a45
03482c6
3698836
2ae9de2
8a82370
d2f71fa
2366575
9de8d78
229147e
2631a98
dde4cd0
37294df
46f2c11
5df5b19
abd6e86
295b3ae
a07b81c
49ef040
136d27a
3a60e46
080f74c
3ca5ffd
9f0c97d
2359255
daef5c6
7bd6e71
599780a
3ce83fd
5998dfd
b42e3dc
8c6ac43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
{ | ||
"flutterSdkVersion": "3.10.0", | ||
"flutterSdkVersion": "3.10.2", | ||
"flavors": {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,18 @@ import 'mobile/mobile_io.dart'; | |
import 'web/stub_web_io.dart' // Stub implementation | ||
if (dart.library.html) 'web/web_io.dart'; | ||
|
||
class SaveStatus { | ||
final int bytesSaved; | ||
final int totalBytes; | ||
final bool? saveResult; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Edit - Having it be null means the download hasn't yet finished. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can rename it to |
||
|
||
SaveStatus({ | ||
required this.bytesSaved, | ||
required this.totalBytes, | ||
this.saveResult, | ||
}); | ||
} | ||
|
||
/// API for I/O operations | ||
/// | ||
/// Opens the platform specific file picker to pick files and folders, and save files using | ||
|
@@ -36,4 +48,6 @@ abstract class ArDriveIO { | |
Future<IOFolder> pickFolder(); | ||
|
||
Future<void> saveFile(IOFile file); | ||
|
||
Stream<SaveStatus> saveFileStream(IOFile file, Completer<bool> finalize); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
import 'dart:async'; | ||
import 'dart:io'; | ||
|
||
import 'package:ardrive_io/ardrive_io.dart'; | ||
import 'package:ardrive_io/src/utils/completer.dart'; | ||
import 'package:file_saver/file_saver.dart' as file_saver; | ||
import 'package:flutter/foundation.dart'; | ||
import 'package:mime/mime.dart' as mime; | ||
import 'package:path/path.dart' as p; | ||
|
||
|
@@ -64,9 +67,19 @@ class MobileIO implements ArDriveIO { | |
} | ||
|
||
@override | ||
Future<void> saveFile(IOFile file) async { | ||
Future<void> saveFile(IOFile file, [bool saveOnAppDirectory = false]) async { | ||
try { | ||
await _fileSaver.save(file); | ||
await _fileSaver.save(file, saveOnAppDirectory: saveOnAppDirectory); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can get rid of this try..catch because it's rethrowing and nothing else. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment for the method below. |
||
} catch (e) { | ||
rethrow; | ||
} | ||
} | ||
|
||
@override | ||
Stream<SaveStatus> saveFileStream( | ||
IOFile file, Completer<bool> finalize) async* { | ||
try { | ||
yield* _fileSaver.saveStream(file, finalize); | ||
} catch (e) { | ||
rethrow; | ||
} | ||
|
@@ -78,12 +91,24 @@ class MobileIO implements ArDriveIO { | |
/// This implementation uses the `file_saver` package. | ||
/// | ||
/// Throws an `FileSystemPermissionDeniedException` when user deny access to storage | ||
/// | ||
/// `saveOnAppDirectory` is not supported on this implementation | ||
class MobileSelectableFolderFileSaver implements FileSaver { | ||
final DartIOFileSaver _dartIOFileSaver; | ||
|
||
MobileSelectableFolderFileSaver({DartIOFileSaver? dartIOFileSaver}) | ||
: _dartIOFileSaver = dartIOFileSaver ?? DartIOFileSaver(); | ||
|
||
@override | ||
Future<void> save(IOFile file) async { | ||
Future<void> save(IOFile file, {bool saveOnAppDirectory = false}) async { | ||
await requestPermissions(); | ||
await verifyPermissions(); | ||
|
||
if (saveOnAppDirectory) { | ||
await _dartIOFileSaver.save(file, saveOnAppDirectory: saveOnAppDirectory); | ||
return; | ||
} | ||
|
||
await file_saver.FileSaver.instance.saveAs( | ||
name: file.name, | ||
bytes: await file.readAsBytes(), | ||
|
@@ -92,13 +117,19 @@ class MobileSelectableFolderFileSaver implements FileSaver { | |
|
||
return; | ||
} | ||
|
||
@override | ||
Stream<SaveStatus> saveStream(IOFile file, Completer<bool> finalize) { | ||
// file_saver doesn't seem to support support saving streams | ||
throw UnimplementedError(); | ||
} | ||
} | ||
|
||
/// Saves a file using the `dart:io` library. | ||
/// It will save on `getDefaultMobileDownloadDir()` | ||
class DartIOFileSaver implements FileSaver { | ||
@override | ||
Future<void> save(IOFile file) async { | ||
Future<void> save(IOFile file, {bool saveOnAppDirectory = false}) async { | ||
await requestPermissions(); | ||
await verifyPermissions(); | ||
|
||
|
@@ -111,24 +142,105 @@ class DartIOFileSaver implements FileSaver { | |
fileName += '.$fileExtension'; | ||
} | ||
|
||
if (saveOnAppDirectory) { | ||
await _saveOnAppDir(file, fileName); | ||
return; | ||
} | ||
|
||
await _saveOnDownloadsDir(file, fileName); | ||
} | ||
|
||
Future<void> _saveOnDownloadsDir(IOFile file, String fileName) async { | ||
/// platform_specific_path/Downloads/ | ||
final defaultDownloadDir = await getDefaultMobileDownloadDir(); | ||
|
||
final newFile = File(defaultDownloadDir + fileName); | ||
final newFile = await nonexistentFile(defaultDownloadDir, file); | ||
|
||
await newFile.writeAsBytes(await file.readAsBytes()); | ||
} | ||
|
||
Future<void> _saveOnAppDir(IOFile file, String fileName) async { | ||
final appDir = await getDefaultAppDir(); | ||
|
||
final newFile = File(appDir + fileName); | ||
|
||
await newFile.writeAsBytes(await file.readAsBytes()); | ||
} | ||
|
||
@override | ||
Stream<SaveStatus> saveStream(IOFile file, Completer<bool> finalize) async* { | ||
var bytesSaved = 0; | ||
final totalBytes = await file.length; | ||
yield SaveStatus( | ||
bytesSaved: bytesSaved, | ||
totalBytes: totalBytes, | ||
); | ||
|
||
try { | ||
await requestPermissions(); | ||
await verifyPermissions(); | ||
|
||
/// platform_specific_path/Downloads/ | ||
final defaultDownloadDir = await getDefaultMobileDownloadDir(); | ||
|
||
final newFile = await nonexistentFile(defaultDownloadDir, file); | ||
|
||
final sink = newFile.openWrite(); | ||
|
||
// NOTE: This is an alternative to `addStream` with lower level control | ||
const flushThresholdBytes = 50 * 1024 * 1024; // 50 MiB | ||
var unflushedDataBytes = 0; | ||
await for (final chunk in file.openReadStream()) { | ||
if (await completerMaybe(finalize) == false) break; | ||
|
||
sink.add(chunk); | ||
unflushedDataBytes += chunk.length; | ||
if (unflushedDataBytes > flushThresholdBytes) { | ||
await sink.flush(); | ||
unflushedDataBytes = 0; | ||
} | ||
|
||
bytesSaved += chunk.length; | ||
yield SaveStatus( | ||
bytesSaved: bytesSaved, | ||
totalBytes: totalBytes, | ||
); | ||
} | ||
await sink.flush(); | ||
await sink.close(); | ||
|
||
final finalizeResult = await finalize.future; | ||
if (!finalizeResult) { | ||
debugPrint('Cancelling saveStream...'); | ||
await newFile.delete(); | ||
} | ||
|
||
yield SaveStatus( | ||
bytesSaved: bytesSaved, | ||
totalBytes: totalBytes, | ||
saveResult: finalizeResult, | ||
); | ||
} catch (e) { | ||
yield SaveStatus( | ||
bytesSaved: bytesSaved, | ||
totalBytes: totalBytes, | ||
saveResult: false, | ||
); | ||
} | ||
} | ||
} | ||
|
||
/// Defines the API for saving `IOFile` on Storage | ||
abstract class FileSaver { | ||
factory FileSaver() { | ||
if (Platform.isAndroid || Platform.isIOS) { | ||
return MobileSelectableFolderFileSaver(); | ||
return DartIOFileSaver(); | ||
} | ||
throw UnsupportedPlatformException( | ||
'The ${Platform.operatingSystem} platform is not supported'); | ||
} | ||
|
||
Future<void> save(IOFile file); | ||
Future<void> save(IOFile file, {bool saveOnAppDirectory = false}); | ||
|
||
Stream<SaveStatus> saveStream(IOFile file, Completer<bool> finalize); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import 'dart:async'; | ||
|
||
Future<T?> completerMaybe<T>(Completer<T> completer) => | ||
completer.isCompleted | ||
? completer.future | ||
: Future.value(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we get rid of this log?