Upload data to the presigned s3 bucket URL
This commit is contained in:
parent
93b1fb446d
commit
617ea34f57
3 changed files with 151 additions and 77 deletions
119
lib/file_upload_manager.dart
Normal file
119
lib/file_upload_manager.dart
Normal file
|
@ -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
lib/models/upload_url.dart
Normal file
28
lib/models/upload_url.dart
Normal file
|
@ -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));
|
||||
}
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue