-
Notifications
You must be signed in to change notification settings - Fork 8
#6006 add path and thumbnails to yust file #399
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
base: master
Are you sure you want to change the base?
Changes from all commits
03c3605
8e43865
0989b8d
95ad14e
170d257
51d924a
6156a0b
d171ae4
96eacf0
361ffd5
c6b6208
909f617
05331c0
65e3e37
550dac2
910aaae
9ce1a1f
c3add2e
fde659f
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,13 +1,32 @@ | ||
| import 'dart:convert'; | ||
| import 'dart:io'; | ||
| import 'dart:typed_data'; | ||
|
|
||
| import 'package:collection/collection.dart'; | ||
| import 'package:json_annotation/json_annotation.dart'; | ||
|
|
||
| import '../../yust.dart'; | ||
|
|
||
| part 'yust_file.g.dart'; | ||
|
|
||
| typedef YustFileJson = Map<String, dynamic>; | ||
| typedef YustFilesJson = List<YustFileJson>; | ||
|
|
||
| /// The size of the thumbnail. | ||
| enum YustFileThumbnailSize { | ||
| small; | ||
|
|
||
| /// Converts a JSON string to a [YustFileThumbnailSize]. | ||
| /// | ||
| /// If the size is not found, [YustFileThumbnailSize.small] is returned. | ||
| static YustFileThumbnailSize fromJson(String size) => | ||
| YustFileThumbnailSize.values.firstWhereOrNull((e) => e.name == size) ?? | ||
| YustFileThumbnailSize.small; | ||
|
|
||
| /// Converts a [YustFileThumbnailSize] to a JSON string. | ||
| String toJson() => name; | ||
| } | ||
|
|
||
| /// A binary file handled by database and file storage. | ||
| /// A file is stored in Firebase Storage and linked to a document in the database. | ||
| /// For offline caching a file can also be stored on the device. | ||
|
|
@@ -31,6 +50,14 @@ class YustFile { | |
| /// On mobile devices, this can also be the time the file was uploaded into device cache. | ||
| DateTime? createdAt; | ||
|
|
||
| /// The path to the file in the storage. | ||
| String? path; | ||
|
|
||
| /// The thumbnails of the file. | ||
| /// | ||
| /// Map of thumbnail size to path in the storage. | ||
| Map<YustFileThumbnailSize, String>? thumbnails; | ||
|
|
||
| /// The binary file. This attribute is used for iOS and Android. For web [bytes] is used instead. | ||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||
| File? file; | ||
|
|
@@ -59,6 +86,10 @@ class YustFile { | |
| @JsonKey(includeFromJson: false, includeToJson: false) | ||
| String? lastError; | ||
|
|
||
| /// True if a thumbnail should be created. | ||
| @JsonKey(includeFromJson: false, includeToJson: false) | ||
| bool? createThumbnail; | ||
|
|
||
| /// True if the files should be stored as a Map of hash and file | ||
| /// inside the linked document. | ||
| /// | ||
|
|
@@ -98,8 +129,11 @@ class YustFile { | |
| this.linkedDocAttribute, | ||
| this.processing = false, | ||
| this.lastError, | ||
| this.createThumbnail, | ||
| this.linkedDocStoresFilesAsMap, | ||
| this.createdAt, | ||
| this.path, | ||
| this.thumbnails, | ||
| bool setCreatedAtToNow = true, | ||
| }) { | ||
| if (setCreatedAtToNow) { | ||
|
|
@@ -124,6 +158,11 @@ class YustFile { | |
| : json['createdAt'] is DateTime | ||
| ? json['createdAt'] as DateTime | ||
| : DateTime.parse(json['createdAt'] as String), | ||
| path: json['path'] as String?, | ||
| thumbnails: (json['thumbnails'] as Map?)?.map( | ||
| (key, value) => | ||
| MapEntry(YustFileThumbnailSize.fromJson(key), value as String), | ||
| ), | ||
| setCreatedAtToNow: false, | ||
| ); | ||
| } | ||
|
|
@@ -141,6 +180,8 @@ class YustFile { | |
| name = file.name; | ||
| hash = file.hash; | ||
| createdAt = file.createdAt; | ||
| path = file.path; | ||
| thumbnails = file.thumbnails; | ||
| } | ||
|
|
||
| /// Converts the file to JSON for local device. Only relevant attributes are converted. | ||
|
|
@@ -154,13 +195,24 @@ class YustFile { | |
| linkedDocPath: json['linkedDocPath'] as String, | ||
| linkedDocAttribute: json['linkedDocAttribute'] as String, | ||
| lastError: json['lastError'] as String?, | ||
| createThumbnail: json['createThumbnail'] == 'true', | ||
| linkedDocStoresFilesAsMap: json['linkedDocStoresFilesAsMap'] == 'true', | ||
| modifiedAt: json['modifiedAt'] != null | ||
| ? DateTime.parse(json['modifiedAt'] as String) | ||
| : null, | ||
| createdAt: json['createdAt'] != null | ||
| ? DateTime.parse(json['createdAt'] as String) | ||
| : null, | ||
| path: json['path'] as String?, | ||
| thumbnails: json['thumbnails'] != null | ||
| ? (jsonDecode(json['thumbnails'] as String) as Map).map( | ||
| (key, value) => MapEntry( | ||
| YustFileThumbnailSize.fromJson(key), | ||
| value as String, | ||
| ), | ||
| ) | ||
| : null, | ||
|
|
||
| setCreatedAtToNow: false, | ||
| ); | ||
| } | ||
|
|
@@ -170,16 +222,22 @@ class YustFile { | |
| /// This is used for offline file handling only (Caching on mobile devices) | ||
| Map<String, String?> toLocalJson() { | ||
| if (name == null) { | ||
| throw ('Error: Each cached file needs a name. Should be unique for each path!'); | ||
| throw YustException( | ||
| 'Error: Each cached file needs a name. Should be unique for each path!', | ||
| ); | ||
| } | ||
| if (devicePath == null) { | ||
| throw ('Error: Device Path has to be a String.'); | ||
| throw YustException('Error: Device Path has to be a String.'); | ||
| } | ||
| if (storageFolderPath == null) { | ||
| throw ('Error: StorageFolderPath has to be set for a successful upload.'); | ||
| throw YustException( | ||
| 'Error: StorageFolderPath has to be set for a successful upload.', | ||
| ); | ||
| } | ||
| if (linkedDocPath == null || linkedDocAttribute == null) { | ||
| throw ('Error: linkedDocPath and linkedDocAttribute have to be set for a successful upload.'); | ||
| throw YustException( | ||
| 'Error: linkedDocPath and linkedDocAttribute have to be set for a successful upload.', | ||
| ); | ||
| } | ||
| return { | ||
| 'name': name, | ||
|
|
@@ -188,14 +246,39 @@ class YustFile { | |
| 'linkedDocAttribute': linkedDocAttribute, | ||
| 'devicePath': devicePath, | ||
| 'lastError': lastError, | ||
| 'createThumbnail': (createThumbnail ?? false).toString(), | ||
| 'linkedDocStoresFilesAsMap': (linkedDocStoresFilesAsMap ?? false) | ||
| .toString(), | ||
| 'modifiedAt': modifiedAt?.toIso8601String(), | ||
| 'createdAt': createdAt?.toIso8601String(), | ||
| 'type': type, | ||
| 'path': path, | ||
| 'thumbnails': thumbnails != null ? jsonEncode(thumbnails) : null, | ||
| }; | ||
| } | ||
|
|
||
| /// Creates a new file with the same properties but with a new URL. | ||
| YustFile copyWithUrl(String? url) => YustFile( | ||
| key: key, | ||
| name: name, | ||
| modifiedAt: modifiedAt, | ||
| url: url, | ||
| hash: hash, | ||
| file: file, | ||
| bytes: bytes, | ||
| devicePath: devicePath, | ||
| storageFolderPath: storageFolderPath, | ||
| linkedDocPath: linkedDocPath, | ||
| linkedDocAttribute: linkedDocAttribute, | ||
| processing: processing, | ||
| lastError: lastError, | ||
| createThumbnail: createThumbnail, | ||
| createdAt: createdAt, | ||
| path: path, | ||
| thumbnails: thumbnails, | ||
| setCreatedAtToNow: false, | ||
| ); | ||
|
|
||
| dynamic operator [](String key) { | ||
| switch (key) { | ||
| case 'name': | ||
|
|
@@ -206,6 +289,9 @@ class YustFile { | |
| return url; | ||
| case 'createdAt': | ||
| return createdAt; | ||
| case 'path': | ||
| return path; | ||
|
Contributor
Author
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. auch rausnehmen |
||
| case 'thumbnails': // Thumbnails should not be accessible | ||
| default: | ||
| throw ArgumentError(); | ||
| } | ||
|
|
@@ -234,4 +320,28 @@ class YustFile { | |
|
|
||
| return name!.contains('.') ? name!.split('.').last : ''; | ||
| } | ||
|
|
||
| bool hasThumbnail() => thumbnails?.isNotEmpty ?? false; | ||
|
Contributor
Author
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. kommentare
Contributor
Author
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. getter |
||
|
|
||
| String? getOriginalUrl() { | ||
| final baseUrl = Yust.fileAccessService.originalCdnBaseUrl; | ||
| final grant = Yust.fileAccessService.getGrantForFile(this); | ||
|
|
||
| if (baseUrl == null || grant == null || path == null) return url; | ||
|
Contributor
Author
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. kommentar wann passiert das |
||
|
|
||
| return '${_tryAppendSlash(baseUrl)}$path?${grant.originalSignedUrlPart}'; | ||
| } | ||
|
|
||
| String? getThumbnailUrl() { | ||
|
Contributor
Author
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. size parameter |
||
| final baseUrl = Yust.fileAccessService.thumbnailCdnBaseUrl; | ||
| final grant = Yust.fileAccessService.getGrantForFile(this); | ||
|
|
||
| if (baseUrl == null || grant == null || !hasThumbnail()) return null; | ||
| final thumbnailPath = thumbnails![YustFileThumbnailSize.small]; | ||
|
|
||
| return '${_tryAppendSlash(baseUrl)}$thumbnailPath?${grant.thumbnailSignedUrlPart}'; | ||
| } | ||
|
|
||
| String? _tryAppendSlash(String baseUrl) => | ||
|
Contributor
Author
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. enforcen im service |
||
| baseUrl.endsWith('/') ? baseUrl : '$baseUrl/'; | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| export './yust_file_access_service_dart.dart' | ||
| if (dart.library.ui) './yust_file_access_service_flutter.dart'; | ||
| export './yust_file_access_service_interface.dart'; |
|
Contributor
Author
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. createGrant 2 CDN configurations & pathPrefix, validUntil Um den Flow zu verstehen |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import 'package:collection/collection.dart'; | ||
|
|
||
| import '../models/yust_file.dart'; | ||
| import '../util/file_access/yust_cdn_configuration.dart'; | ||
| import '../util/file_access/yust_file_access_grant.dart'; | ||
| import '../util/google_cloud_cdn_helper.dart'; | ||
| import 'yust_file_access_service_interface.dart'; | ||
|
|
||
| class YustFileAccessService implements IYustFileAccessService { | ||
| @override | ||
| final String? originalCdnBaseUrl; | ||
|
|
||
| @override | ||
| final String? thumbnailCdnBaseUrl; | ||
|
|
||
| @override | ||
| List<YustFileAccessGrant> grants = []; | ||
|
|
||
| @override | ||
| Future<String?> Function(YustFile)? generateDownloadUrl; | ||
|
|
||
| YustFileAccessService({ | ||
| required this.originalCdnBaseUrl, | ||
| required this.thumbnailCdnBaseUrl, | ||
| }); | ||
|
|
||
| @override | ||
| String createSignedUrlForFile({ | ||
| required String path, | ||
| required String name, | ||
| required Duration validFor, | ||
| required YustCdnConfiguration cdnConfiguration, | ||
| Map<String, String>? additionalQueryParams, | ||
| }) { | ||
| final helper = GoogleCloudCdnHelper( | ||
| baseUrl: cdnConfiguration.baseUrl, | ||
| keyName: cdnConfiguration.keyName, | ||
| keyBase64: cdnConfiguration.keyBase64, | ||
| ); | ||
| return helper.signFilePath( | ||
| objectPath: '$path/$name', | ||
| validFor: validFor, | ||
| additionalQueryParams: additionalQueryParams, | ||
| ); | ||
| } | ||
|
|
||
| @override | ||
| String createSignedUrlPartForFolder({ | ||
| required String path, | ||
| required Duration validFor, | ||
| required YustCdnConfiguration cdnConfiguration, | ||
| }) { | ||
| final helper = GoogleCloudCdnHelper( | ||
| baseUrl: cdnConfiguration.baseUrl, | ||
| keyName: cdnConfiguration.keyName, | ||
| keyBase64: cdnConfiguration.keyBase64, | ||
| ); | ||
| return helper.signPrefix(prefixPath: path, validFor: validFor); | ||
| } | ||
|
|
||
| @override | ||
| void setGrants(List<YustFileAccessGrant> grants) { | ||
| this.grants = grants; | ||
| } | ||
|
|
||
| @override | ||
| YustFileAccessGrant? getGrantForFile(YustFile file) { | ||
| return grants.firstWhereOrNull( | ||
| (grant) => file.path?.startsWith(grant.pathPrefix) ?? false, | ||
| ); | ||
| } | ||
| } |
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.
Url deprecaten
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.
oder follow up :)