Sfoglia il codice sorgente

Upload data to the presigned s3 bucket URL

Vishnu Mohandas 5 anni fa
parent
commit
617ea34f57
3 ha cambiato i file con 151 aggiunte e 77 eliminazioni
  1. 119 0
      lib/file_upload_manager.dart
  2. 28 0
      lib/models/upload_url.dart
  3. 4 77
      lib/photo_sync_manager.dart

+ 119 - 0
lib/file_upload_manager.dart

@@ -0,0 +1,119 @@
+import 'dart:convert';
+import 'dart:io' as io;
+import 'package:dio/dio.dart';
+import 'package:logging/logging.dart';
+import 'package:photos/core/configuration.dart';
+import 'package:photos/core/constants.dart';
+import 'package:photos/models/file.dart';
+import 'package:photos/models/upload_url.dart';
+import 'package:photos/utils/crypto_util.dart';
+import 'package:photos/utils/file_name_util.dart';
+import 'package:photos/utils/file_util.dart';
+
+class FileUploadManager {
+  final _logger = Logger("FileUploadManager");
+  final _dio = Dio();
+
+  Future<UploadURL> getUploadURL() {
+    return Dio()
+        .get(
+          Configuration.instance.getHttpEndpoint() +
+              "/encrypted-files/upload-url",
+          options: Options(
+              headers: {"X-Auth-Token": Configuration.instance.getToken()}),
+        )
+        .then((response) => UploadURL.fromMap(response.data));
+  }
+
+  Future<String> putFile(UploadURL uploadURL, io.File file) async {
+    _logger.info("Putting file to " + uploadURL.url);
+    return Dio()
+        .put(uploadURL.url,
+            data: file.openRead(),
+            options: Options(headers: {
+              Headers.contentLengthHeader: await file.length(),
+            }))
+        .catchError((e) {
+      _logger.severe(e);
+    }).then((value) {
+      return uploadURL.objectKey;
+    });
+  }
+
+  Future<File> encryptAndUploadFile(File file) async {
+    final key = Configuration.instance.getKey();
+
+    final encryptedFileName = file.generatedID.toString() + ".aes";
+    final tempDirectory = Configuration.instance.getTempDirectory();
+    final encryptedFilePath = tempDirectory + encryptedFileName;
+    await CryptoUtil.encryptDataToFile(
+        await getBytesFromDisk(file), encryptedFilePath, key);
+
+    final fileUploadURL = await getUploadURL();
+    String fileObjectKey =
+        await putFile(fileUploadURL, io.File(encryptedFilePath));
+
+    final thumbnailData = (await (await file.getAsset())
+        .thumbDataWithSize(THUMBNAIL_LARGE_SIZE, THUMBNAIL_LARGE_SIZE));
+    final encryptedThumbnailName =
+        file.generatedID.toString() + "_thumbnail.aes";
+    final encryptedThumbnailPath = tempDirectory + encryptedThumbnailName;
+    await CryptoUtil.encryptDataToFile(
+        thumbnailData, encryptedThumbnailPath, key);
+
+    final thumbnailUploadURL = await getUploadURL();
+    String thumbnailObjectKey =
+        await putFile(thumbnailUploadURL, io.File(encryptedThumbnailPath));
+
+    final metadata = jsonEncode(file.getMetadata());
+    final metadataIV =
+        CryptoUtil.getBase64EncodedSecureRandomString(length: 16);
+    final encryptedMetadata =
+        CryptoUtil.encryptToBase64(metadata, key, metadataIV);
+    final data = {
+      "fileObjectKey": fileObjectKey,
+      "thumbnailObjectKey": thumbnailObjectKey,
+      "metadata": encryptedMetadata,
+      "metadataIV": metadataIV,
+    };
+    return _dio
+        .post(
+      Configuration.instance.getHttpEndpoint() + "/encrypted-files",
+      options:
+          Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
+      data: data,
+    )
+        .then((response) {
+      io.File(encryptedFilePath).deleteSync();
+      io.File(encryptedThumbnailPath).deleteSync();
+      final data = response.data;
+      file.uploadedFileID = data["id"];
+      file.updationTime = data["updationTime"];
+      file.ownerID = data["ownerID"];
+      return file;
+    });
+  }
+
+  Future<File> uploadFile(File localPhoto) async {
+    final title = getJPGFileNameForHEIC(localPhoto);
+    final formData = FormData.fromMap({
+      "file": MultipartFile.fromBytes(await getBytesFromDisk(localPhoto),
+          filename: title),
+      "deviceFileID": localPhoto.localID,
+      "deviceFolder": localPhoto.deviceFolder,
+      "title": title,
+      "creationTime": localPhoto.creationTime,
+      "modificationTime": localPhoto.modificationTime,
+    });
+    return _dio
+        .post(
+      Configuration.instance.getHttpEndpoint() + "/files",
+      options:
+          Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
+      data: formData,
+    )
+        .then((response) {
+      return File.fromJson(response.data);
+    });
+  }
+}

+ 28 - 0
lib/models/upload_url.dart

@@ -0,0 +1,28 @@
+import 'dart:convert';
+
+class UploadURL {
+  final String url;
+  final String objectKey;
+
+  UploadURL(this.url, this.objectKey);
+  Map<String, dynamic> toMap() {
+    return {
+      'url': url,
+      'objectKey': objectKey,
+    };
+  }
+
+  factory UploadURL.fromMap(Map<String, dynamic> map) {
+    if (map == null) return null;
+
+    return UploadURL(
+      map['url'],
+      map['objectKey'],
+    );
+  }
+
+  String toJson() => json.encode(toMap());
+
+  factory UploadURL.fromJson(String source) =>
+      UploadURL.fromMap(json.decode(source));
+}

+ 4 - 77
lib/photo_sync_manager.dart

@@ -1,20 +1,18 @@
 import 'dart:async';
 import 'dart:convert';
-import 'dart:io' as io;
 import 'dart:math';
 
 import 'package:logging/logging.dart';
-import 'package:photos/core/constants.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/events/photo_upload_event.dart';
 import 'package:photos/events/user_authenticated_event.dart';
 import 'package:photos/file_repository.dart';
 import 'package:photo_manager/photo_manager.dart';
+import 'package:photos/file_upload_manager.dart';
 import 'package:photos/models/file_type.dart';
 import 'package:photos/utils/crypto_util.dart';
 import 'package:photos/utils/file_name_util.dart';
-import 'package:photos/utils/file_util.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 import 'package:dio/dio.dart';
 import 'package:photos/models/file.dart';
@@ -26,6 +24,7 @@ class PhotoSyncManager {
   final _logger = Logger("PhotoSyncManager");
   final _dio = Dio();
   final _db = FilesDB.instance;
+  final _uploadManager = FileUploadManager();
   bool _isSyncInProgress = false;
   Future<void> _existingSync;
   SharedPreferences _prefs;
@@ -207,9 +206,9 @@ class PhotoSyncManager {
         }
         var uploadedFile;
         if (Configuration.instance.hasOptedForE2E()) {
-          uploadedFile = await _uploadEncryptedFile(file);
+          uploadedFile = await _uploadManager.encryptAndUploadFile(file);
         } else {
-          uploadedFile = await _uploadFile(file);
+          uploadedFile = await _uploadManager.uploadFile(file);
         }
         await _db.update(file.generatedID, uploadedFile.uploadedFileID,
             uploadedFile.updationTime);
@@ -295,78 +294,6 @@ class PhotoSyncManager {
     }
   }
 
-  Future<File> _uploadEncryptedFile(File file) async {
-    final key = Configuration.instance.getKey();
-
-    final encryptedFileName = file.generatedID.toString() + ".aes";
-    final tempDirectory = Configuration.instance.getTempDirectory();
-    final encryptedFilePath = tempDirectory + encryptedFileName;
-    await CryptoUtil.encryptDataToFile(
-        await getBytesFromDisk(file), encryptedFilePath, key);
-
-    final thumbnailData = (await (await file.getAsset())
-        .thumbDataWithSize(THUMBNAIL_LARGE_SIZE, THUMBNAIL_LARGE_SIZE));
-    final encryptedThumbnailName =
-        file.generatedID.toString() + "_thumbnail.aes";
-    final encryptedThumbnailPath = tempDirectory + encryptedThumbnailName;
-    await CryptoUtil.encryptDataToFile(
-        thumbnailData, encryptedThumbnailPath, key);
-
-    final metadata = jsonEncode(file.getMetadata());
-    final metadataIV =
-        CryptoUtil.getBase64EncodedSecureRandomString(length: 16);
-    final encryptedMetadata =
-        CryptoUtil.encryptToBase64(metadata, key, metadataIV);
-    final formData = FormData.fromMap({
-      "file": MultipartFile.fromFileSync(encryptedFilePath,
-          filename: encryptedFileName),
-      "thumbnail": MultipartFile.fromFileSync(encryptedThumbnailPath,
-          filename: encryptedThumbnailName),
-      "metadata": encryptedMetadata,
-      "metadataIV": metadataIV,
-    });
-
-    return _dio
-        .post(
-      Configuration.instance.getHttpEndpoint() + "/encrypted-files",
-      options:
-          Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
-      data: formData,
-    )
-        .then((response) {
-      io.File(encryptedFilePath).deleteSync();
-      io.File(encryptedThumbnailPath).deleteSync();
-      final data = response.data;
-      file.uploadedFileID = data["id"];
-      file.updationTime = data["updationTime"];
-      file.ownerID = data["ownerID"];
-      return file;
-    });
-  }
-
-  Future<File> _uploadFile(File localPhoto) async {
-    final title = getJPGFileNameForHEIC(localPhoto);
-    final formData = FormData.fromMap({
-      "file": MultipartFile.fromBytes(await getBytesFromDisk(localPhoto),
-          filename: title),
-      "deviceFileID": localPhoto.localID,
-      "deviceFolder": localPhoto.deviceFolder,
-      "title": title,
-      "creationTime": localPhoto.creationTime,
-      "modificationTime": localPhoto.modificationTime,
-    });
-    return _dio
-        .post(
-      Configuration.instance.getHttpEndpoint() + "/files",
-      options:
-          Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
-      data: formData,
-    )
-        .then((response) {
-      return File.fromJson(response.data);
-    });
-  }
-
   Future<void> _deletePhotosOnServer() async {
     return _db.getAllDeleted().then((deletedPhotos) async {
       for (File deletedPhoto in deletedPhotos) {