Bladeren bron

Compress thumbnails fetched from the server

Vishnu Mohandas 4 jaren geleden
bovenliggende
commit
a1e68d54a6
3 gewijzigde bestanden met toevoegingen van 58 en 25 verwijderingen
  1. 1 0
      lib/core/constants.dart
  2. 32 16
      lib/ui/thumbnail_widget.dart
  3. 25 9
      lib/utils/file_util.dart

+ 1 - 0
lib/core/constants.dart

@@ -1,5 +1,6 @@
 const int THUMBNAIL_SMALL_SIZE = 256;
 const int THUMBNAIL_SMALL_SIZE = 256;
 const int THUMBNAIL_SMALL_SIZE_QUALITY = 80;
 const int THUMBNAIL_SMALL_SIZE_QUALITY = 80;
 const int THUMBNAIL_LARGE_SIZE = 512;
 const int THUMBNAIL_LARGE_SIZE = 512;
+const int THUMBNAIL_DATA_LIMIT = 100 * 1024;
 const String SENTRY_DSN =
 const String SENTRY_DSN =
     "http://96780dc0b00f4c69a16c02e90d379996@3.211.17.56/2";
     "http://96780dc0b00f4c69a16c02e90d379996@3.211.17.56/2";

+ 32 - 16
lib/ui/thumbnail_widget.dart

@@ -1,4 +1,5 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_image_compress/flutter_image_compress.dart';
 import 'package:photos/core/cache/image_cache.dart';
 import 'package:photos/core/cache/image_cache.dart';
 import 'package:photos/core/cache/thumbnail_cache.dart';
 import 'package:photos/core/cache/thumbnail_cache.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/db/files_db.dart';
@@ -142,25 +143,40 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
         _hasLoadedThumbnail = true;
         _hasLoadedThumbnail = true;
         return;
         return;
       }
       }
-      getThumbnailFromServer(widget.file).then((file) {
-        final imageProvider = Image.file(file).image;
-        if (mounted) {
-          precacheImage(imageProvider, context).then((value) {
-            if (mounted) {
-              setState(() {
-                _imageProvider = imageProvider;
-                _hasLoadedThumbnail = true;
-              });
-            }
-          }).catchError((e) {
-            _logger.severe("Could not load image " + widget.file.toString());
-            _encounteredErrorLoadingThumbnail = true;
-          });
-        }
-      });
+      _getThumbnailFromServer();
     }
     }
   }
   }
 
 
+  void _getThumbnailFromServer() {
+    getThumbnailFromServer(widget.file).then((file) async {
+      var imageProvider;
+      if (file.lengthSync() > THUMBNAIL_DATA_LIMIT) {
+        final compressed = await FlutterImageCompress.compressWithFile(
+          file.path,
+          quality: 25,
+          minHeight: THUMBNAIL_SMALL_SIZE,
+          minWidth: THUMBNAIL_SMALL_SIZE,
+        );
+        imageProvider = Image.memory(compressed).image;
+      } else {
+        imageProvider = Image.file(file).image;
+      }
+      if (mounted) {
+        precacheImage(imageProvider, context).then((value) {
+          if (mounted) {
+            setState(() {
+              _imageProvider = imageProvider;
+              _hasLoadedThumbnail = true;
+            });
+          }
+        }).catchError((e) {
+          _logger.severe("Could not load image " + widget.file.toString());
+          _encounteredErrorLoadingThumbnail = true;
+        });
+      }
+    });
+  }
+
   @override
   @override
   void didUpdateWidget(ThumbnailWidget oldWidget) {
   void didUpdateWidget(ThumbnailWidget oldWidget) {
     super.didUpdateWidget(oldWidget);
     super.didUpdateWidget(oldWidget);

+ 25 - 9
lib/utils/file_util.dart

@@ -27,7 +27,7 @@ import 'package:photos/services/sync_service.dart';
 
 
 import 'crypto_util.dart';
 import 'crypto_util.dart';
 
 
-final logger = Logger("FileUtil");
+final _logger = Logger("FileUtil");
 
 
 Future<void> deleteFiles(List<File> files) async {
 Future<void> deleteFiles(List<File> files) async {
   final localIDs = List<String>();
   final localIDs = List<String>();
@@ -179,7 +179,7 @@ Future<io.File> getThumbnailFromServer(File file) async {
 
 
 Future<io.File> _downloadAndDecrypt(File file, BaseCacheManager cacheManager,
 Future<io.File> _downloadAndDecrypt(File file, BaseCacheManager cacheManager,
     {ProgressCallback progressCallback}) async {
     {ProgressCallback progressCallback}) async {
-  logger.info("Downloading file " + file.uploadedFileID.toString());
+  _logger.info("Downloading file " + file.uploadedFileID.toString());
   final encryptedFilePath = Configuration.instance.getTempDirectory() +
   final encryptedFilePath = Configuration.instance.getTempDirectory() +
       file.generatedID.toString() +
       file.generatedID.toString() +
       ".encrypted";
       ".encrypted";
@@ -199,21 +199,21 @@ Future<io.File> _downloadAndDecrypt(File file, BaseCacheManager cacheManager,
       )
       )
       .then((response) async {
       .then((response) async {
     if (response.statusCode != 200) {
     if (response.statusCode != 200) {
-      logger.warning("Could not download file: ", response.toString());
+      _logger.warning("Could not download file: ", response.toString());
       return null;
       return null;
     } else if (!encryptedFile.existsSync()) {
     } else if (!encryptedFile.existsSync()) {
-      logger.warning("File was not downloaded correctly.");
+      _logger.warning("File was not downloaded correctly.");
       return null;
       return null;
     }
     }
-    logger.info("File downloaded: " + file.uploadedFileID.toString());
-    logger.info("Download speed: " +
+    _logger.info("File downloaded: " + file.uploadedFileID.toString());
+    _logger.info("Download speed: " +
         (io.File(encryptedFilePath).lengthSync() /
         (io.File(encryptedFilePath).lengthSync() /
                 (DateTime.now().millisecondsSinceEpoch - startTime))
                 (DateTime.now().millisecondsSinceEpoch - startTime))
             .toString() +
             .toString() +
         "kBps");
         "kBps");
     await CryptoUtil.decryptFile(encryptedFilePath, decryptedFilePath,
     await CryptoUtil.decryptFile(encryptedFilePath, decryptedFilePath,
         Sodium.base642bin(file.fileDecryptionHeader), decryptFileKey(file));
         Sodium.base642bin(file.fileDecryptionHeader), decryptFileKey(file));
-    logger.info("File decrypted: " + file.uploadedFileID.toString());
+    _logger.info("File decrypted: " + file.uploadedFileID.toString());
     encryptedFile.deleteSync();
     encryptedFile.deleteSync();
     var fileExtension = extension(file.title).substring(1).toLowerCase();
     var fileExtension = extension(file.title).substring(1).toLowerCase();
     var outputFile = decryptedFile;
     var outputFile = decryptedFile;
@@ -241,6 +241,9 @@ Future<io.File> _downloadAndDecrypt(File file, BaseCacheManager cacheManager,
 }
 }
 
 
 Future<io.File> _downloadAndDecryptThumbnail(File file) async {
 Future<io.File> _downloadAndDecryptThumbnail(File file) async {
+  _logger.info("Downloading thumbnail for " + file.uploadedFileID.toString());
+  _logger.info("Downloads in progress " +
+      thumbnailDownloadsInProgress.length.toString());
   final temporaryPath = Configuration.instance.getTempDirectory() +
   final temporaryPath = Configuration.instance.getTempDirectory() +
       file.generatedID.toString() +
       file.generatedID.toString() +
       "_thumbnail.decrypted";
       "_thumbnail.decrypted";
@@ -250,18 +253,31 @@ Future<io.File> _downloadAndDecryptThumbnail(File file) async {
       .then((_) async {
       .then((_) async {
     final encryptedFile = io.File(temporaryPath);
     final encryptedFile = io.File(temporaryPath);
     final thumbnailDecryptionKey = decryptFileKey(file);
     final thumbnailDecryptionKey = decryptFileKey(file);
-    final data = CryptoUtil.decryptChaCha(
+    var data = CryptoUtil.decryptChaCha(
       encryptedFile.readAsBytesSync(),
       encryptedFile.readAsBytesSync(),
       thumbnailDecryptionKey,
       thumbnailDecryptionKey,
       Sodium.base642bin(file.thumbnailDecryptionHeader),
       Sodium.base642bin(file.thumbnailDecryptionHeader),
     );
     );
+    if (data.length > THUMBNAIL_DATA_LIMIT) {
+      data = await FlutterImageCompress.compressWithList(
+        data,
+        quality: 50,
+        minHeight: THUMBNAIL_SMALL_SIZE,
+        minWidth: THUMBNAIL_SMALL_SIZE,
+      );
+    }
     encryptedFile.deleteSync();
     encryptedFile.deleteSync();
-    return ThumbnailCacheManager().putFile(
+    final cachedThumbnail = ThumbnailCacheManager().putFile(
       file.getThumbnailUrl(),
       file.getThumbnailUrl(),
       data,
       data,
       eTag: file.getThumbnailUrl(),
       eTag: file.getThumbnailUrl(),
       maxAge: Duration(days: 365),
       maxAge: Duration(days: 365),
     );
     );
+    thumbnailDownloadsInProgress.remove(file.uploadedFileID);
+    return cachedThumbnail;
+  }).catchError((e, s) {
+    _logger.severe("Error downloading thumbnail ", e, s);
+    thumbnailDownloadsInProgress.remove(file.uploadedFileID);
   });
   });
 }
 }