file_util.dart 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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:photos/core/cache/image_cache.dart';
  10. import 'package:path/path.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. return 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(ente.File file, BaseCacheManager cacheManager,
  91. {ProgressCallback progressCallback}) async {
  92. return downloadAndDecrypt(file, progressCallback: progressCallback)
  93. .then((decryptedFile) async {
  94. if (decryptedFile == null) {
  95. return null;
  96. }
  97. var decryptedFilePath = decryptedFile.path;
  98. var fileExtension = "unknown";
  99. try {
  100. fileExtension = extension(file.title).substring(1).toLowerCase();
  101. } catch (e) {
  102. _logger.severe("Could not capture file extension");
  103. }
  104. var outputFile = decryptedFile;
  105. if ((fileExtension == "unknown" && file.fileType == FileType.image) ||
  106. (io.Platform.isAndroid && fileExtension == "heic")) {
  107. outputFile = await FlutterImageCompress.compressAndGetFile(
  108. decryptedFilePath,
  109. decryptedFilePath + ".jpg",
  110. keepExif: true,
  111. );
  112. decryptedFile.deleteSync();
  113. }
  114. final cachedFile = await cacheManager.putFile(
  115. file.getDownloadUrl(),
  116. outputFile.readAsBytesSync(),
  117. eTag: file.getDownloadUrl(),
  118. maxAge: Duration(days: 365),
  119. fileExtension: fileExtension,
  120. );
  121. outputFile.deleteSync();
  122. fileDownloadsInProgress.remove(file.uploadedFileID);
  123. return cachedFile;
  124. }).catchError((e) {
  125. fileDownloadsInProgress.remove(file.uploadedFileID);
  126. });
  127. }
  128. Future<Uint8List> compressThumbnail(Uint8List thumbnail) {
  129. return FlutterImageCompress.compressWithList(
  130. thumbnail,
  131. minHeight: kCompressedThumbnailResolution,
  132. minWidth: kCompressedThumbnailResolution,
  133. quality: 25,
  134. );
  135. }
  136. void clearCache(ente.File file) {
  137. if (file.fileType == FileType.video) {
  138. VideoCacheManager.instance.removeFile(file.getDownloadUrl());
  139. } else {
  140. DefaultCacheManager().removeFile(file.getDownloadUrl());
  141. }
  142. final cachedThumbnail = io.File(
  143. Configuration.instance.getThumbnailCacheDirectory() +
  144. "/" +
  145. file.uploadedFileID.toString());
  146. if (cachedThumbnail.existsSync()) {
  147. cachedThumbnail.deleteSync();
  148. }
  149. }