file_download_util.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import 'dart:io' as io;
  2. import 'dart:typed_data';
  3. import "package:computer/computer.dart";
  4. import 'package:dio/dio.dart';
  5. import 'package:logging/logging.dart';
  6. import 'package:photos/core/configuration.dart';
  7. import 'package:photos/core/network/network.dart';
  8. import 'package:photos/models/file.dart' as ente;
  9. import 'package:photos/services/collections_service.dart';
  10. import 'package:photos/utils/crypto_util.dart';
  11. import "package:photos/utils/data_util.dart";
  12. final _logger = Logger("file_download_util");
  13. Future<io.File?> downloadAndDecrypt(
  14. ente.File file, {
  15. ProgressCallback? progressCallback,
  16. }) {
  17. final String logPrefix = 'File-${file.uploadedFileID}:';
  18. _logger.info('$logPrefix starting download');
  19. final encryptedFilePath = Configuration.instance.getTempDirectory() +
  20. file.generatedID.toString() +
  21. ".encrypted";
  22. final encryptedFile = io.File(encryptedFilePath);
  23. final startTime = DateTime.now().millisecondsSinceEpoch;
  24. return NetworkClient.instance
  25. .getDio()
  26. .download(
  27. file.downloadUrl,
  28. encryptedFilePath,
  29. options: Options(
  30. headers: {"X-Auth-Token": Configuration.instance.getToken()},
  31. ),
  32. onReceiveProgress: progressCallback,
  33. )
  34. .then((response) async {
  35. if (response.statusCode != 200) {
  36. _logger.warning('$logPrefix download failed ${response.toString()}');
  37. return null;
  38. } else if (!encryptedFile.existsSync()) {
  39. _logger.warning('$logPrefix incomplete download, file not found');
  40. return null;
  41. }
  42. final int sizeInBytes = ((file.fileSize ?? 0) > 0)
  43. ? file.fileSize!
  44. : await encryptedFile.length();
  45. final double speedInKBps = sizeInBytes /
  46. 1024.0 /
  47. ((DateTime.now().millisecondsSinceEpoch - startTime) / 1000);
  48. _logger.info(
  49. "$logPrefix download completed: ${formatBytes(sizeInBytes)}, avg speed: ${speedInKBps.toStringAsFixed(2)} KB/s",
  50. );
  51. final decryptedFilePath = Configuration.instance.getTempDirectory() +
  52. file.generatedID.toString() +
  53. ".decrypted";
  54. try {
  55. await CryptoUtil.decryptFile(
  56. encryptedFilePath,
  57. decryptedFilePath,
  58. CryptoUtil.base642bin(file.fileDecryptionHeader!),
  59. getFileKey(file),
  60. );
  61. } catch (e, s) {
  62. _logger.severe("failed to decrypt file", e, s);
  63. return null;
  64. }
  65. _logger.info('$logPrefix decryption completed');
  66. await encryptedFile.delete();
  67. return io.File(decryptedFilePath);
  68. });
  69. }
  70. Uint8List getFileKey(ente.File file) {
  71. final encryptedKey = CryptoUtil.base642bin(file.encryptedKey!);
  72. final nonce = CryptoUtil.base642bin(file.keyDecryptionNonce!);
  73. final collectionKey =
  74. CollectionsService.instance.getCollectionKey(file.collectionID!);
  75. return CryptoUtil.decryptSync(encryptedKey, collectionKey, nonce);
  76. }
  77. Future<Uint8List> getFileKeyUsingBgWorker(ente.File file) async {
  78. final collectionKey =
  79. CollectionsService.instance.getCollectionKey(file.collectionID!);
  80. return await Computer.shared().compute(
  81. _decryptFileKey,
  82. param: <String, dynamic>{
  83. "encryptedKey": file.encryptedKey,
  84. "keyDecryptionNonce": file.keyDecryptionNonce,
  85. "collectionKey": collectionKey,
  86. },
  87. );
  88. }
  89. Uint8List _decryptFileKey(Map<String, dynamic> args) {
  90. final encryptedKey = CryptoUtil.base642bin(args["encryptedKey"]);
  91. final nonce = CryptoUtil.base642bin(args["keyDecryptionNonce"]);
  92. return CryptoUtil.decryptSync(
  93. encryptedKey,
  94. args["collectionKey"],
  95. nonce,
  96. );
  97. }