From c3885453520c6258ee391f05389dbc628a3615ab Mon Sep 17 00:00:00 2001 From: Mohammad Bagher Fakouri Date: Sun, 31 Mar 2024 00:10:38 +0330 Subject: [PATCH] feat: Add `ParseXFile` for cross-platform `XFile` support (#990) --- packages/dart/CHANGELOG.md | 6 + packages/dart/lib/parse_server_sdk.dart | 60 +------ .../dart/lib/src/base/parse_constants.dart | 2 +- packages/dart/lib/src/objects/parse_file.dart | 2 +- .../dart/lib/src/objects/parse_file_web.dart | 2 +- .../dart/lib/src/objects/parse_x_file.dart | 158 ++++++++++++++++++ packages/dart/pubspec.yaml | 5 +- 7 files changed, 175 insertions(+), 60 deletions(-) create mode 100644 packages/dart/lib/src/objects/parse_x_file.dart diff --git a/packages/dart/CHANGELOG.md b/packages/dart/CHANGELOG.md index 748d734b1..782a2a0f5 100644 --- a/packages/dart/CHANGELOG.md +++ b/packages/dart/CHANGELOG.md @@ -1,3 +1,9 @@ +## [6.4.0](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-6.3.0...dart-6.4.0) (2024-03-30) + +### Features + +* Add `ParseXFile` for cross-platform `XFile` support ([#990](https://github.com/parse-community/Parse-SDK-Flutter/pull/990)) + ## [6.3.0](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-6.2.0...dart-6.3.0) (2023-11-11) ### Features diff --git a/packages/dart/lib/parse_server_sdk.dart b/packages/dart/lib/parse_server_sdk.dart index 847c1bd61..555393ded 100644 --- a/packages/dart/lib/parse_server_sdk.dart +++ b/packages/dart/lib/parse_server_sdk.dart @@ -6,9 +6,10 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:collection/collection.dart'; +import 'package:cross_file/cross_file.dart'; import 'package:dio/dio.dart'; import 'package:meta/meta.dart'; -import 'package:mime_type/mime_type.dart'; +import 'package:mime/mime.dart'; import 'package:path/path.dart' as path; import 'package:sembast/sembast.dart'; import 'package:sembast/sembast_io.dart'; @@ -27,109 +28,58 @@ export 'src/network/parse_dio_client.dart'; export 'src/network/parse_http_client.dart'; part 'src/base/parse_constants.dart'; - part 'src/data/parse_core_data.dart'; - part 'src/data/parse_subclass_handler.dart'; - part 'src/enums/parse_enum_api_rq.dart'; - part 'src/network/options.dart'; - part 'src/network/parse_client.dart'; - part 'src/network/parse_connectivity.dart'; - part 'src/network/parse_live_query.dart'; - part 'src/network/parse_query.dart'; - part 'src/objects/parse_acl.dart'; - part 'src/objects/parse_array.dart'; - part 'src/objects/parse_base.dart'; - part 'src/objects/parse_cloneable.dart'; - part 'src/objects/parse_config.dart'; - part 'src/objects/parse_error.dart'; - +part 'src/objects/parse_exception.dart'; part 'src/objects/parse_file.dart'; - -part 'src/objects/parse_number.dart'; - part 'src/objects/parse_file_base.dart'; - part 'src/objects/parse_file_web.dart'; - part 'src/objects/parse_function.dart'; - part 'src/objects/parse_geo_point.dart'; - part 'src/objects/parse_installation.dart'; - +part 'src/objects/parse_number.dart'; part 'src/objects/parse_object.dart'; - -part 'src/objects/parse_exception.dart'; - part 'src/objects/parse_operation/parse_add_operation.dart'; - part 'src/objects/parse_operation/parse_add_relation_operation.dart'; - part 'src/objects/parse_operation/parse_add_unique_operation.dart'; - part 'src/objects/parse_operation/parse_increment_operation.dart'; - part 'src/objects/parse_operation/parse_operation.dart'; - part 'src/objects/parse_operation/parse_remove_operation.dart'; - part 'src/objects/parse_operation/parse_remove_relation_operation.dart'; - part 'src/objects/parse_relation.dart'; - part 'src/objects/parse_response.dart'; - part 'src/objects/parse_save_state_aware_child.dart'; - part 'src/objects/parse_session.dart'; - part 'src/objects/parse_user.dart'; - +part 'src/objects/parse_x_file.dart'; part 'src/objects/response/parse_error_response.dart'; - part 'src/objects/response/parse_exception_response.dart'; - part 'src/objects/response/parse_response_builder.dart'; - part 'src/objects/response/parse_response_utils.dart'; - part 'src/objects/response/parse_success_no_results.dart'; - part 'src/storage/core_store.dart'; - part 'src/storage/core_store_memory.dart'; - part 'src/storage/core_store_sem_impl.dart'; - part 'src/storage/xxtea_codec.dart'; - part 'src/utils/parse_date_format.dart'; - part 'src/utils/parse_decoder.dart'; - part 'src/utils/parse_encoder.dart'; - part 'src/utils/parse_live_list.dart'; - part 'src/utils/parse_logger.dart'; - part 'src/utils/parse_login_helpers.dart'; - part 'src/utils/parse_utils.dart'; - part 'src/utils/valuable.dart'; class Parse { diff --git a/packages/dart/lib/src/base/parse_constants.dart b/packages/dart/lib/src/base/parse_constants.dart index f0170438d..a1dcfed0f 100644 --- a/packages/dart/lib/src/base/parse_constants.dart +++ b/packages/dart/lib/src/base/parse_constants.dart @@ -1,7 +1,7 @@ part of flutter_parse_sdk; // Library -const String keySdkVersion = '6.3.0'; +const String keySdkVersion = '6.4.0'; const String keyLibraryName = 'Flutter Parse SDK'; // End Points diff --git a/packages/dart/lib/src/objects/parse_file.dart b/packages/dart/lib/src/objects/parse_file.dart index faa666d34..e70da5ff5 100644 --- a/packages/dart/lib/src/objects/parse_file.dart +++ b/packages/dart/lib/src/objects/parse_file.dart @@ -82,7 +82,7 @@ class ParseFile extends ParseFileBase { final Map headers = { HttpHeaders.contentTypeHeader: - mime(file!.path) ?? 'application/octet-stream', + lookupMimeType(file!.path) ?? 'application/octet-stream', HttpHeaders.contentLengthHeader: '${file!.lengthSync()}', }; diff --git a/packages/dart/lib/src/objects/parse_file_web.dart b/packages/dart/lib/src/objects/parse_file_web.dart index c45f5a9c0..c092ff4d9 100644 --- a/packages/dart/lib/src/objects/parse_file_web.dart +++ b/packages/dart/lib/src/objects/parse_file_web.dart @@ -61,7 +61,7 @@ class ParseWebFile extends ParseFileBase { final Map headers = { HttpHeaders.contentTypeHeader: - mime(url ?? name) ?? 'application/octet-stream', + lookupMimeType(url ?? name) ?? 'application/octet-stream', }; try { final String uri = ParseCoreData().serverUrl + _path; diff --git a/packages/dart/lib/src/objects/parse_x_file.dart b/packages/dart/lib/src/objects/parse_x_file.dart new file mode 100644 index 000000000..550877603 --- /dev/null +++ b/packages/dart/lib/src/objects/parse_x_file.dart @@ -0,0 +1,158 @@ +part of flutter_parse_sdk; + +class ParseXFile extends ParseFileBase { + /// Creates a new file base XFile + /// + /// {https://docs.parseplatform.org/rest/guide/#files/} + ParseXFile(this.file, + {String? name, + String? url, + bool? debug, + ParseClient? client, + bool? autoSendSessionId}) + : super( + name: file != null ? path.basename(file.path) : name!, + url: url, + debug: debug, + client: client, + autoSendSessionId: autoSendSessionId, + ); + + XFile? file; + CancelToken? _cancelToken; + ProgressCallback? _progressCallback; + + Future loadStorage() async { + // Web not need load storage. + if (parseIsWeb) { + return this; + } + + final XFile possibleFile = XFile('${ParseCoreData().fileDirectory}/$name'); + // ignore: avoid_slow_async_io + final bool exists = await File(possibleFile.path).exists(); + + if (exists) { + file = possibleFile; + } else { + file = null; + } + + return this; + } + + @override + Future download({ProgressCallback? progressCallback}) async { + if (url == null) { + return this; + } + + progressCallback ??= _progressCallback; + + _cancelToken = CancelToken(); + + if (parseIsWeb) { + final ParseNetworkByteResponse response = await _client.getBytes( + url!, + onReceiveProgress: progressCallback, + cancelToken: _cancelToken, + ); + + if (response.bytes != null) { + file = XFile.fromData(response.bytes as Uint8List); + } + } else { + file = XFile('${ParseCoreData().fileDirectory}/$name'); + await File(file!.path).create(); + + final ParseNetworkByteResponse response = await _client.getBytes( + url!, + onReceiveProgress: progressCallback, + cancelToken: _cancelToken, + ); + await File(file!.path).writeAsBytes(response.bytes!); + } + + return this; + } + + /// Uploads a file to Parse Server + @override + Future upload({ProgressCallback? progressCallback}) async { + if (saved) { + //Creates a Fake Response to return the correct result + final Map response = { + 'url': url!, + 'name': name + }; + return handleResponse( + this, + ParseNetworkResponse(data: json.encode(response), statusCode: 201), + ParseApiRQ.upload, + _debug, + parseClassName); + } + + progressCallback ??= _progressCallback; + + _cancelToken = CancelToken(); + Map headers; + if (parseIsWeb) { + headers = { + HttpHeaders.contentTypeHeader: file?.mimeType ?? + lookupMimeType(url ?? file?.name ?? name, + headerBytes: await file?.readAsBytes()) ?? + 'application/octet-stream', + }; + } else { + headers = { + HttpHeaders.contentTypeHeader: file?.mimeType ?? + lookupMimeType(file!.path) ?? + 'application/octet-stream', + HttpHeaders.contentLengthHeader: '${await file!.length()}', + }; + } + + try { + final String uri = ParseCoreData().serverUrl + _path; + + Stream>? data; + if (parseIsWeb) { + data = Stream>.fromIterable( + >[await file!.readAsBytes()]); + } else { + data = file!.openRead(); + } + + final ParseNetworkResponse response = await _client.postBytes( + uri, + options: ParseNetworkOptions(headers: headers), + data: data, + onSendProgress: progressCallback, + cancelToken: _cancelToken, + ); + if (response.statusCode == 201) { + final Map map = json.decode(response.data); + url = map['url'].toString(); + name = map['name'].toString(); + } + return handleResponse( + this, response, ParseApiRQ.upload, _debug, parseClassName); + } on Exception catch (e) { + return handleException(e, ParseApiRQ.upload, _debug, parseClassName); + } + } + + /// Cancels the current request (upload or download of file). + @override + void cancel([dynamic reason]) { + _cancelToken?.cancel(reason); + _cancelToken = null; + } + + /// Add Progress Callback + @override + void progressCallback(ProgressCallback progressCallback) { + _progressCallback = progressCallback; + } +} diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index c972690c3..b50734a96 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -1,6 +1,6 @@ name: parse_server_sdk description: The Dart SDK to connect to Parse Server. Build your apps faster with Parse Platform, the complete application stack. -version: 6.3.0 +version: 6.4.0 homepage: https://parseplatform.org repository: https://github.com/parse-community/Parse-SDK-Flutter issue_tracker: https://github.com/parse-community/Parse-SDK-Flutter/issues @@ -34,11 +34,12 @@ dependencies: uuid: ^3.0.7 meta: ^1.8.0 path: ^1.8.2 - mime_type: ^1.0.0 + mime: ^1.0.4 timezone: ^0.9.2 universal_io: ^2.2.0 xxtea: ^2.1.0 collection: ^1.16.0 + cross_file: ^0.3.3+6 dev_dependencies: lints: ^2.0.1