fix: clean up temp files after upload completes

MetadataStripper, MediaCompressor, and EncryptFiles create intermediate
temp files in cacheDir during the upload pipeline, but these were never
deleted after the upload finished. Over time this causes unbounded disk
growth. Wrap upload calls in try/finally to ensure all intermediate temp
files (compressed, stripped, encrypted) are deleted regardless of
success or failure.

https://claude.ai/code/session_01YS3dbZ1k84N2EHS3AruYRV
This commit is contained in:
Claude
2026-03-24 04:37:41 +00:00
parent 7874893938
commit cba65344b9
@@ -31,10 +31,12 @@ import com.vitorpamplona.amethyst.service.uploads.nip96.Nip96Uploader
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
import com.vitorpamplona.quartz.utils.Log
import com.vitorpamplona.quartz.utils.ciphers.NostrCipher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import okhttp3.OkHttpClient
import java.io.File
import kotlin.coroutines.cancellation.CancellationException
sealed class UploadingState {
@@ -324,6 +326,26 @@ class UploadOrchestrator {
return strippingResult.uri
}
/**
* Deletes a temporary file created during the upload pipeline if its URI
* differs from the original (meaning it's an intermediate temp file, not the user's content).
*/
private fun deleteTempUri(
tempUri: Uri,
originalUri: Uri,
) {
if (tempUri == originalUri) return
try {
val path = tempUri.path ?: return
val file = File(path)
if (file.exists() && file.delete()) {
Log.d("UploadOrchestrator", "Deleted temp file: $path")
}
} catch (e: Exception) {
Log.w("UploadOrchestrator", "Failed to delete temp file: ${tempUri.path}", e)
}
}
suspend fun upload(
uri: Uri,
mimeType: String?,
@@ -343,10 +365,17 @@ class UploadOrchestrator {
stripAfterCompression(uri, compressed, mimeType, compressionQuality, stripMetadata, onStrippingFailed, context)
?: return error(R.string.upload_cancelled)
return when (server.type) {
ServerType.NIP95 -> uploadNIP95(finalUri, compressed.contentType, null, null, context)
ServerType.NIP96 -> uploadNIP96(finalUri, compressed.contentType, compressed.size, alt, contentWarningReason, server.baseUrl, null, null, account, context)
ServerType.Blossom -> uploadBlossom(finalUri, compressed.contentType, compressed.size, alt, contentWarningReason, server.baseUrl, null, null, account, context)
try {
return when (server.type) {
ServerType.NIP95 -> uploadNIP95(finalUri, compressed.contentType, null, null, context)
ServerType.NIP96 -> uploadNIP96(finalUri, compressed.contentType, compressed.size, alt, contentWarningReason, server.baseUrl, null, null, account, context)
ServerType.Blossom -> uploadBlossom(finalUri, compressed.contentType, compressed.size, alt, contentWarningReason, server.baseUrl, null, null, account, context)
}
} finally {
// Clean up intermediate temp files created by stripping and compression.
// Delete stripped file first (if different from compressed), then compressed (if different from original).
deleteTempUri(finalUri, compressed.uri)
deleteTempUri(compressed.uri, uri)
}
}
@@ -372,10 +401,17 @@ class UploadOrchestrator {
val encrypted = EncryptFiles().encryptFile(context, finalUri, encrypt)
return when (server.type) {
ServerType.NIP95 -> uploadNIP95(encrypted.uri, encrypted.contentType, compressed.contentType, encrypted.originalHash, context)
ServerType.NIP96 -> uploadNIP96(encrypted.uri, encrypted.contentType, encrypted.size, alt, contentWarningReason, server.baseUrl, compressed.contentType, encrypted.originalHash, account, context)
ServerType.Blossom -> uploadBlossom(encrypted.uri, encrypted.contentType, encrypted.size, alt, contentWarningReason, server.baseUrl, compressed.contentType, encrypted.originalHash, account, context)
try {
return when (server.type) {
ServerType.NIP95 -> uploadNIP95(encrypted.uri, encrypted.contentType, compressed.contentType, encrypted.originalHash, context)
ServerType.NIP96 -> uploadNIP96(encrypted.uri, encrypted.contentType, encrypted.size, alt, contentWarningReason, server.baseUrl, compressed.contentType, encrypted.originalHash, account, context)
ServerType.Blossom -> uploadBlossom(encrypted.uri, encrypted.contentType, encrypted.size, alt, contentWarningReason, server.baseUrl, compressed.contentType, encrypted.originalHash, account, context)
}
} finally {
// Clean up all intermediate temp files: encrypted, stripped, and compressed.
deleteTempUri(encrypted.uri, finalUri)
deleteTempUri(finalUri, compressed.uri)
deleteTempUri(compressed.uri, uri)
}
}
}