I am developing an Android app and need to save file to cache directory. I get file through ActivityResultContracts
, which returns "content://" uri. Bellow is the function to download (virtual-) file to the cache directory
suspend fun createFileSchemeFromContentSchemeUri(context: Context, uri: Uri): Uri? = withContext(Dispatchers.IO) {
if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
try {
val stream: InputStream? = getInputStream(context, uri)
val mimetype: String? = context.contentResolver.getType(uri)
val fileExtension: String? = MimeTypeMap.getSingleton()
.getExtensionFromMimeType(mimetype)
.let {
if (it == null) googleVndMimeTypeToExtension.get(mimetype)
else it
}
var filename: String? = context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
cursor.moveToFirst()
cursor.getString(nameIndex)
}
if (stream == null || filename == null || fileExtension == null) return@withContext null
filename = filename.removeSuffix(".$fileExtension")
val file = File.createTempFile(filename, ".$fileExtension", context.externalCacheDir)
stream.use { `in` ->
file.outputStream().use { out ->
val buf = ByteArray(1024 * 1024) // 1MB
var len: Int
while (`in`.read(buf).also { len = it } > 0) {
out.write(buf, 0, len)
}
}
}
file.toUri()
} catch (_: Exception) {
null
}
} else null
}
The problem is, that in before copying to output file it is possible to retrieve mimetype (not always, but most of the time), but after uri was returned and the same method way to read mimetype is used, i get null
What is the way to preserve original mimetype or "set" it manually?
I also tried to use FileDescriptor, but it didn't work
val fileDescriptor = outputStream.fd
fileDescriptor?.let {
val fileOutputStream = FileOutputStream(fileDescriptor)
fileOutputStream.use {
FileUtils.copy(
context.contentResolver.openFileDescriptor(uri, "r")?.fileDescriptor,
fileDescriptor
)
}
fileOutputStream.flush()
fileOutputStream.close()
outputStream.close()
}
The answer is pretty easy. Turns out, that context.contentResolver.getType(uri)
only works for "content://" based uri-s, which was not right for my case after file was saved to local cache (it became "file://"). Therefor, I updated method to handle both content- and file- based uri-s
fun getMimeType(context: Context, uri: Uri): String? {
if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
return context.contentResolver.getType(uri)
} else if (uri.scheme == ContentResolver.SCHEME_FILE) {
val fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension)
} else return null
}