file_util.dart 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import 'dart:io' as io;
  2. import 'dart:typed_data';
  3. import 'package:path/path.dart';
  4. import 'package:dio/dio.dart';
  5. import 'package:flutter_cache_manager/flutter_cache_manager.dart';
  6. import 'package:flutter_image_compress/flutter_image_compress.dart';
  7. import 'package:photo_manager/photo_manager.dart';
  8. import 'package:photos/core/cache/image_cache.dart';
  9. import 'package:photos/core/cache/thumbnail_cache.dart';
  10. import 'package:photos/core/cache/thumbnail_cache_manager.dart';
  11. import 'package:photos/core/cache/video_cache_manager.dart';
  12. import 'package:photos/core/configuration.dart';
  13. import 'package:photos/core/constants.dart';
  14. import 'package:photos/db/files_db.dart';
  15. import 'package:photos/models/encrypted_file_attributes.dart';
  16. import 'package:photos/models/encryption_attribute.dart';
  17. import 'package:photos/models/file.dart';
  18. import 'package:photos/models/file_type.dart';
  19. import 'crypto_util.dart';
  20. Future<void> deleteFiles(List<File> files,
  21. {bool deleteEveryWhere = false}) async {
  22. await PhotoManager.editor
  23. .deleteWithIds(files.map((file) => file.localID).toList());
  24. for (File file in files) {
  25. deleteEveryWhere
  26. ? await FilesDB.instance.markForDeletion(file)
  27. : await FilesDB.instance.delete(file);
  28. }
  29. }
  30. void preloadFile(File file) {
  31. if (file.fileType == FileType.video) {
  32. return;
  33. }
  34. if (file.localID == null) {
  35. getFileFromServer(file);
  36. } else {
  37. if (FileLruCache.get(file) == null) {
  38. file.getAsset().then((asset) {
  39. asset.file.then((assetFile) {
  40. FileLruCache.put(file, assetFile);
  41. });
  42. });
  43. }
  44. }
  45. }
  46. void preloadLocalFileThumbnail(File file) {
  47. if (file.localID == null ||
  48. ThumbnailLruCache.get(file, THUMBNAIL_SMALL_SIZE) != null) {
  49. return;
  50. }
  51. file.getAsset().then((asset) {
  52. asset
  53. .thumbDataWithSize(THUMBNAIL_SMALL_SIZE, THUMBNAIL_SMALL_SIZE)
  54. .then((data) {
  55. ThumbnailLruCache.put(file, THUMBNAIL_SMALL_SIZE, data);
  56. });
  57. });
  58. }
  59. Future<io.File> getNativeFile(File file) async {
  60. if (file.localID == null) {
  61. return getFileFromServer(file);
  62. } else {
  63. return file.getAsset().then((asset) => asset.file);
  64. }
  65. }
  66. Future<Uint8List> getBytes(File file, {int quality = 100}) async {
  67. if (file.localID == null) {
  68. return getFileFromServer(file).then((file) => file.readAsBytesSync());
  69. } else {
  70. return await getBytesFromDisk(file, quality: quality);
  71. }
  72. }
  73. Future<Uint8List> getBytesFromDisk(File file, {int quality = 100}) async {
  74. final originalBytes = (await file.getAsset()).originBytes;
  75. if (extension(file.title) == ".HEIC" || quality != 100) {
  76. return originalBytes.then((bytes) {
  77. return FlutterImageCompress.compressWithList(bytes, quality: quality)
  78. .then((converted) {
  79. return Uint8List.fromList(converted);
  80. });
  81. });
  82. } else {
  83. return originalBytes;
  84. }
  85. }
  86. Future<io.File> getFileFromServer(File file,
  87. {ProgressCallback progressCallback}) async {
  88. final cacheManager = file.fileType == FileType.video
  89. ? VideoCacheManager()
  90. : DefaultCacheManager();
  91. if (!file.isEncrypted) {
  92. return cacheManager.getSingleFile(file.getDownloadUrl());
  93. } else {
  94. return cacheManager.getFileFromCache(file.getDownloadUrl()).then((info) {
  95. if (info == null) {
  96. return _downloadAndDecrypt(
  97. file,
  98. cacheManager,
  99. progressCallback: progressCallback,
  100. );
  101. } else {
  102. return info.file;
  103. }
  104. });
  105. }
  106. }
  107. Future<io.File> getThumbnailFromServer(File file) async {
  108. if (!file.isEncrypted) {
  109. return ThumbnailCacheManager()
  110. .getSingleFile(file.getThumbnailUrl())
  111. .then((data) {
  112. ThumbnailFileLruCache.put(file, data);
  113. return data;
  114. });
  115. } else {
  116. return ThumbnailCacheManager()
  117. .getFileFromCache(file.getThumbnailUrl())
  118. .then((info) {
  119. if (info == null) {
  120. return _downloadAndDecryptThumbnail(file).then((data) {
  121. ThumbnailFileLruCache.put(file, data);
  122. return data;
  123. });
  124. } else {
  125. ThumbnailFileLruCache.put(file, info.file);
  126. return info.file;
  127. }
  128. });
  129. }
  130. }
  131. Future<io.File> _downloadAndDecrypt(File file, BaseCacheManager cacheManager,
  132. {ProgressCallback progressCallback}) async {
  133. final encryptedFilePath = Configuration.instance.getTempDirectory() +
  134. file.generatedID.toString() +
  135. ".encrypted";
  136. final decryptedFilePath = Configuration.instance.getTempDirectory() +
  137. file.generatedID.toString() +
  138. ".decrypted";
  139. final encryptedFile = io.File(encryptedFilePath);
  140. final decryptedFile = io.File(decryptedFilePath);
  141. return Dio()
  142. .download(
  143. file.getDownloadUrl(),
  144. encryptedFilePath,
  145. onReceiveProgress: progressCallback,
  146. )
  147. .then((_) async {
  148. var attributes = ChaChaAttributes(
  149. EncryptionAttribute(base64: file.fileDecryptionParams.header),
  150. EncryptionAttribute(
  151. bytes: await CryptoUtil.decrypt(
  152. file.fileDecryptionParams.encryptedKey,
  153. Configuration.instance.getBase64EncodedKey(),
  154. file.fileDecryptionParams.nonce,
  155. )));
  156. await CryptoUtil.chachaDecrypt(encryptedFile, decryptedFile, attributes);
  157. encryptedFile.deleteSync();
  158. decryptedFile.deleteSync();
  159. final fileExtension = extension(file.title).substring(1).toLowerCase();
  160. return cacheManager.putFile(
  161. file.getDownloadUrl(),
  162. decryptedFile.readAsBytesSync(),
  163. eTag: file.getDownloadUrl(),
  164. maxAge: Duration(days: 365),
  165. fileExtension: fileExtension,
  166. );
  167. });
  168. }
  169. Future<io.File> _downloadAndDecryptThumbnail(File file) async {
  170. final temporaryPath = Configuration.instance.getTempDirectory() +
  171. file.generatedID.toString() +
  172. "_thumbnail.decrypted";
  173. return Dio().download(file.getThumbnailUrl(), temporaryPath).then((_) async {
  174. final encryptedFile = io.File(temporaryPath);
  175. final data = await CryptoUtil.decryptWithDecryptionParams(
  176. encryptedFile.readAsBytesSync(),
  177. file.thumbnailDecryptionParams,
  178. Configuration.instance.getBase64EncodedKey());
  179. encryptedFile.deleteSync();
  180. return ThumbnailCacheManager().putFile(
  181. file.getThumbnailUrl(),
  182. data,
  183. eTag: file.getThumbnailUrl(),
  184. maxAge: Duration(days: 365),
  185. );
  186. });
  187. }