DefaultFileService: store just sent file

This commit is contained in:
Benoit Marty 2020-12-09 12:20:48 +01:00 committed by Benoit Marty
parent ca7796114c
commit 1c43f92e49
2 changed files with 44 additions and 32 deletions

View file

@ -111,7 +111,7 @@ internal class DefaultFileService @Inject constructor(
// ensure we use unique file name by using URL (mapped to suitable file name) // ensure we use unique file name by using URL (mapped to suitable file name)
// Also we need to add extension for the FileProvider, if not it lot's of app that it's // Also we need to add extension for the FileProvider, if not it lot's of app that it's
// shared with will not function well (even if mime type is passed in the intent) // shared with will not function well (even if mime type is passed in the intent)
getFiles(url, fileName, mimeType, elementToDecrypt) getFiles(url, fileName, mimeType, elementToDecrypt != null)
}.flatMap { cachedFiles -> }.flatMap { cachedFiles ->
if (!cachedFiles.file.exists()) { if (!cachedFiles.file.exists()) {
val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: return@flatMap Try.Failure(IllegalArgumentException("url is null")) val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: return@flatMap Try.Failure(IllegalArgumentException("url is null"))
@ -202,24 +202,29 @@ internal class DefaultFileService @Inject constructor(
}.toCancelable() }.toCancelable()
} }
/* fun storeDataFor(mxcUrl: String,
fun storeDataFor(url: String, mimeType: String?, inputStream: InputStream) { filename: String?,
val file = File(downloadFolder, fileForUrl(url, mimeType)) mimeType: String?,
val source = inputStream.source().buffer() originalFile: File,
file.sink().buffer().let { sink -> encryptedFile: File?) {
source.use { input -> val files = getFiles(mxcUrl, filename, mimeType, encryptedFile != null)
sink.use { output -> if (encryptedFile != null) {
output.writeAll(input) // We switch the two files here, original file it the decrypted file
} files.decryptedFile?.let { originalFile.copyTo(it) }
} encryptedFile.copyTo(files.file)
} else {
// Just copy the original file
originalFile.copyTo(files.file)
} }
} }
*/
private fun safeFileName(fileName: String, mimeType: String?): String { private fun safeFileName(fileName: String?, mimeType: String?): String {
return buildString { return buildString {
// filename has to be safe for the Android System // filename has to be safe for the Android System
val result = fileName.replace("[^a-z A-Z0-9\\\\.\\-]".toRegex(), "_") val result = fileName
?.replace("[^a-z A-Z0-9\\\\.\\-]".toRegex(), "_")
?.takeIf { it.isNotEmpty() }
?: DEFAULT_FILENAME
append(result) append(result)
// Check that the extension is correct regarding the mimeType // Check that the extension is correct regarding the mimeType
val extensionFromMime = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) } val extensionFromMime = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) }
@ -252,23 +257,23 @@ internal class DefaultFileService @Inject constructor(
} }
private fun getFiles(mxcUrl: String, private fun getFiles(mxcUrl: String,
fileName: String, fileName: String?,
mimeType: String?, mimeType: String?,
elementToDecrypt: ElementToDecrypt?): CachedFiles { isEncrypted: Boolean): CachedFiles {
val hashFolder = mxcUrl.md5() val hashFolder = mxcUrl.md5()
val safeFileName = safeFileName(fileName, mimeType) val safeFileName = safeFileName(fileName, mimeType)
return if (elementToDecrypt == null) { return if (isEncrypted) {
// Clear file
CachedFiles(
File(downloadFolder, "$hashFolder/$safeFileName"),
null
)
} else {
// Encrypted file // Encrypted file
CachedFiles( CachedFiles(
File(downloadFolder, "$hashFolder/$ENCRYPTED_FILENAME"), File(downloadFolder, "$hashFolder/$ENCRYPTED_FILENAME"),
File(decryptedFolder, "$hashFolder/$safeFileName"), File(decryptedFolder, "$hashFolder/$safeFileName"),
) )
} else {
// Clear file
CachedFiles(
File(downloadFolder, "$hashFolder/$safeFileName"),
null
)
} }
} }
@ -277,7 +282,7 @@ internal class DefaultFileService @Inject constructor(
mimeType: String?, mimeType: String?,
elementToDecrypt: ElementToDecrypt?): FileService.FileState { elementToDecrypt: ElementToDecrypt?): FileService.FileState {
mxcUrl ?: return FileService.FileState.UNKNOWN mxcUrl ?: return FileService.FileState.UNKNOWN
if (getFiles(mxcUrl, fileName, mimeType, elementToDecrypt).file.exists()) return FileService.FileState.IN_CACHE if (getFiles(mxcUrl, fileName, mimeType, elementToDecrypt != null).file.exists()) return FileService.FileState.IN_CACHE
val isDownloading = synchronized(ongoing) { val isDownloading = synchronized(ongoing) {
ongoing[mxcUrl] != null ongoing[mxcUrl] != null
} }
@ -295,7 +300,7 @@ internal class DefaultFileService @Inject constructor(
mxcUrl ?: return null mxcUrl ?: return null
// this string could be extracted no? // this string could be extracted no?
val authority = "${context.packageName}.mx-sdk.fileprovider" val authority = "${context.packageName}.mx-sdk.fileprovider"
val targetFile = getFiles(mxcUrl, fileName, mimeType, elementToDecrypt).getClearFile() val targetFile = getFiles(mxcUrl, fileName, mimeType, elementToDecrypt != null).getClearFile()
if (!targetFile.exists()) return null if (!targetFile.exists()) return null
return FileProvider.getUriForFile(context, authority, targetFile) return FileProvider.getUriForFile(context, authority, targetFile)
} }
@ -319,5 +324,7 @@ internal class DefaultFileService @Inject constructor(
companion object { companion object {
private const val ENCRYPTED_FILENAME = "encrypted.bin" private const val ENCRYPTED_FILENAME = "encrypted.bin"
// The extension would be added from the mimetype
private const val DEFAULT_FILENAME = "file"
} }
} }

View file

@ -174,14 +174,15 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
} }
} }
val encryptedFile: File?
val contentUploadResponse = if (params.isEncrypted) { val contentUploadResponse = if (params.isEncrypted) {
Timber.v("## FileService: Encrypt file") Timber.v("## FileService: Encrypt file")
val tmpEncrypted = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir) encryptedFile = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
.also { filesToDelete.add(it) } .also { filesToDelete.add(it) }
uploadedFileEncryptedFileInfo = uploadedFileEncryptedFileInfo =
MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), attachment.getSafeMimeType(), tmpEncrypted) { read, total -> MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), attachment.getSafeMimeType(), encryptedFile) { read, total ->
notifyTracker(params) { notifyTracker(params) {
contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong()) contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong())
} }
@ -190,19 +191,23 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
Timber.v("## FileService: Uploading file") Timber.v("## FileService: Uploading file")
fileUploader fileUploader
.uploadFile(tmpEncrypted, attachment.name, "application/octet-stream", progressListener) .uploadFile(encryptedFile, attachment.name, "application/octet-stream", progressListener)
} else { } else {
Timber.v("## FileService: Clear file") Timber.v("## FileService: Clear file")
encryptedFile = null
fileUploader fileUploader
.uploadFile(fileToUpload, attachment.name, attachment.getSafeMimeType(), progressListener) .uploadFile(fileToUpload, attachment.name, attachment.getSafeMimeType(), progressListener)
} }
Timber.v("## FileService: Update cache storage for ${contentUploadResponse.contentUri}") Timber.v("## FileService: Update cache storage for ${contentUploadResponse.contentUri}")
try { try {
/* TODO fileService.storeDataFor(
context.contentResolver.openInputStream(attachment.queryUri)?.let { mxcUrl = contentUploadResponse.contentUri,
fileService.storeDataFor(contentUploadResponse.contentUri, params.attachment.getSafeMimeType(), it) filename = params.attachment.name,
} */ mimeType = params.attachment.getSafeMimeType(),
originalFile = workingFile,
encryptedFile = encryptedFile
)
Timber.v("## FileService: cache storage updated") Timber.v("## FileService: cache storage updated")
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.e(failure, "## FileService: Failed to update file cache") Timber.e(failure, "## FileService: Failed to update file cache")