file_util.dart 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import 'dart:async';
  2. import 'dart:io' as io;
  3. import 'dart:typed_data';
  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:logging/logging.dart';
  8. import 'package:motionphoto/motionphoto.dart';
  9. import 'package:path/path.dart';
  10. import 'package:photos/core/cache/image_cache.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/models/file.dart' as ente;
  15. import 'package:photos/models/file_type.dart';
  16. import 'package:photos/utils/thumbnail_util.dart';
  17. import 'package:photos/utils/file_download_util.dart';
  18. final _logger = Logger("FileUtil");
  19. void preloadFile(ente.File file) {
  20. if (file.fileType == FileType.video) {
  21. return;
  22. }
  23. getFile(file);
  24. }
  25. Future<io.File> getFile(ente.File file) async {
  26. if (file.isRemoteFile()) {
  27. return getFileFromServer(file);
  28. } else {
  29. final cachedFile = FileLruCache.get(file);
  30. if (cachedFile == null) {
  31. final diskFile = await _getLocalDiskFile(file);
  32. FileLruCache.put(file, diskFile);
  33. return diskFile;
  34. }
  35. return cachedFile;
  36. }
  37. }
  38. Future<io.File> getLiveVideo(ente.File file) async {
  39. Motionphoto.getLivePhotoFile(file.localID);
  40. }
  41. Future<io.File> _getLocalDiskFile(ente.File file) async {
  42. if (file.isSharedMediaToAppSandbox()) {
  43. var localFile = io.File(getSharedMediaFilePath(file));
  44. return localFile.exists().then((exist) {
  45. return exist ? localFile : null;
  46. });
  47. } else {
  48. return file.getAsset().then((asset) async {
  49. if (asset == null || !(await asset.exists)) {
  50. return null;
  51. }
  52. return asset.file;
  53. });
  54. }
  55. }
  56. String getSharedMediaFilePath(ente.File file) {
  57. return Configuration.instance.getSharedMediaCacheDirectory() +
  58. "/" +
  59. file.localID.replaceAll(kSharedMediaIdentifier, '');
  60. }
  61. void preloadThumbnail(ente.File file) {
  62. if (file.isRemoteFile()) {
  63. getThumbnailFromServer(file);
  64. } else {
  65. getThumbnailFromLocal(file);
  66. }
  67. }
  68. final Map<int, Future<io.File>> fileDownloadsInProgress =
  69. Map<int, Future<io.File>>();
  70. Future<io.File> getFileFromServer(ente.File file,
  71. {ProgressCallback progressCallback}) async {
  72. final cacheManager = file.fileType == FileType.video
  73. ? VideoCacheManager.instance
  74. : DefaultCacheManager();
  75. return cacheManager.getFileFromCache(file.getDownloadUrl()).then((info) {
  76. if (info == null) {
  77. if (!fileDownloadsInProgress.containsKey(file.uploadedFileID)) {
  78. fileDownloadsInProgress[file.uploadedFileID] = _downloadAndCache(
  79. file,
  80. cacheManager,
  81. progressCallback: progressCallback,
  82. );
  83. }
  84. return fileDownloadsInProgress[file.uploadedFileID];
  85. } else {
  86. return info.file;
  87. }
  88. });
  89. }
  90. Future<io.File> _downloadAndCache(
  91. ente.File file, BaseCacheManager cacheManager,
  92. {ProgressCallback progressCallback}) async {
  93. return downloadAndDecrypt(file, progressCallback: progressCallback)
  94. .then((decryptedFile) async {
  95. if (decryptedFile == null) {
  96. return null;
  97. }
  98. var decryptedFilePath = decryptedFile.path;
  99. var fileExtension = "unknown";
  100. try {
  101. fileExtension = extension(file.title).substring(1).toLowerCase();
  102. } catch (e) {
  103. _logger.severe("Could not capture file extension");
  104. }
  105. var outputFile = decryptedFile;
  106. if ((fileExtension == "unknown" && file.fileType == FileType.image) ||
  107. (io.Platform.isAndroid && fileExtension == "heic")) {
  108. outputFile = await FlutterImageCompress.compressAndGetFile(
  109. decryptedFilePath,
  110. decryptedFilePath + ".jpg",
  111. keepExif: true,
  112. );
  113. decryptedFile.deleteSync();
  114. }
  115. final cachedFile = await cacheManager.putFile(
  116. file.getDownloadUrl(),
  117. outputFile.readAsBytesSync(),
  118. eTag: file.getDownloadUrl(),
  119. maxAge: Duration(days: 365),
  120. fileExtension: fileExtension,
  121. );
  122. outputFile.deleteSync();
  123. fileDownloadsInProgress.remove(file.uploadedFileID);
  124. return cachedFile;
  125. }).catchError((e) {
  126. fileDownloadsInProgress.remove(file.uploadedFileID);
  127. });
  128. }
  129. Future<Uint8List> compressThumbnail(Uint8List thumbnail) {
  130. return FlutterImageCompress.compressWithList(
  131. thumbnail,
  132. minHeight: kCompressedThumbnailResolution,
  133. minWidth: kCompressedThumbnailResolution,
  134. quality: 25,
  135. );
  136. }
  137. void clearCache(ente.File file) {
  138. if (file.fileType == FileType.video) {
  139. VideoCacheManager.instance.removeFile(file.getDownloadUrl());
  140. } else {
  141. DefaultCacheManager().removeFile(file.getDownloadUrl());
  142. }
  143. final cachedThumbnail = io.File(
  144. Configuration.instance.getThumbnailCacheDirectory() +
  145. "/" +
  146. file.uploadedFileID.toString());
  147. if (cachedThumbnail.existsSync()) {
  148. cachedThumbnail.deleteSync();
  149. }
  150. }