fixed crash when opening large PSD

This commit is contained in:
Thibault Deckers 2021-04-21 10:30:31 +09:00
parent fba090ae1f
commit 492d9964ad
2 changed files with 36 additions and 17 deletions

View file

@ -2,7 +2,9 @@ package deckers.thibault.aves.metadata
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.util.Log
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.MimeTypes import deckers.thibault.aves.utils.MimeTypes
import deckers.thibault.aves.utils.StorageUtils import deckers.thibault.aves.utils.StorageUtils
import java.io.File import java.io.File
@ -13,6 +15,8 @@ import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
object Metadata { object Metadata {
private val LOG_TAG = LogUtils.createTag<Metadata>()
// Pattern to extract latitude & longitude from a video location tag (cf ISO 6709) // Pattern to extract latitude & longitude from a video location tag (cf ISO 6709)
// Examples: // Examples:
// "+37.5090+127.0243/" (Samsung) // "+37.5090+127.0243/" (Samsung)
@ -96,10 +100,10 @@ object Metadata {
return dateMillis return dateMillis
} }
// opening large TIFF files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1), // opening large PSD/TIFF files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1),
// so we define an arbitrary threshold to avoid a crash on launch. // so we define an arbitrary threshold to avoid a crash on launch.
// It is not clear whether it is because of the file itself or its metadata. // It is not clear whether it is because of the file itself or its metadata.
private const val tiffSizeBytesMax = 100 * (1 shl 20) // MB private const val fileSizeBytesMax = 100 * (1 shl 20) // MB
// we try and read metadata from large files by copying an arbitrary amount from its beginning // we try and read metadata from large files by copying an arbitrary amount from its beginning
// to a temporary file, and reusing that preview file for all metadata reading purposes // to a temporary file, and reusing that preview file for all metadata reading purposes
@ -108,25 +112,38 @@ object Metadata {
private val previewFiles = HashMap<Uri, File>() private val previewFiles = HashMap<Uri, File>()
private fun getSafeUri(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): Uri { private fun getSafeUri(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): Uri {
if (mimeType != MimeTypes.TIFF) return uri return when (mimeType) {
// formats known to yield OOM for large files
if (sizeBytes != null && sizeBytes < tiffSizeBytesMax) return uri MimeTypes.PSD_VND,
MimeTypes.PSD_X,
var previewFile = previewFiles[uri] MimeTypes.TIFF -> {
if (previewFile == null) { if (sizeBytes != null && sizeBytes < fileSizeBytesMax) {
previewFile = File.createTempFile("aves", null, context.cacheDir).apply { // small enough to be safe as it is
deleteOnExit() uri
outputStream().use { outputStream -> } else {
StorageUtils.openInputStream(context, uri)?.use { inputStream -> // make a preview from the beginning of the file,
val b = ByteArray(previewSize) // hoping the metadata is accessible in the copied chunk
inputStream.read(b, 0, previewSize) Log.d(LOG_TAG, "use a preview for uri=$uri mimeType=$mimeType size=$sizeBytes")
outputStream.write(b) var previewFile = previewFiles[uri]
if (previewFile == null) {
previewFile = File.createTempFile("aves", null, context.cacheDir).apply {
deleteOnExit()
outputStream().use { outputStream ->
StorageUtils.openInputStream(context, uri)?.use { inputStream ->
val b = ByteArray(previewSize)
inputStream.read(b, 0, previewSize)
outputStream.write(b)
}
}
}
previewFiles[uri] = previewFile
} }
Uri.fromFile(previewFile)
} }
} }
previewFiles[uri] = previewFile // *probably* safe
else -> uri
} }
return Uri.fromFile(previewFile)
} }
fun openSafeInputStream(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): InputStream? { fun openSafeInputStream(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): InputStream? {

View file

@ -14,6 +14,8 @@ object MimeTypes {
private const val ICO = "image/x-icon" private const val ICO = "image/x-icon"
const val JPEG = "image/jpeg" const val JPEG = "image/jpeg"
const val PNG = "image/png" const val PNG = "image/png"
const val PSD_VND = "image/vnd.adobe.photoshop"
const val PSD_X = "image/x-photoshop"
const val TIFF = "image/tiff" const val TIFF = "image/tiff"
private const val WBMP = "image/vnd.wap.wbmp" private const val WBMP = "image/vnd.wap.wbmp"
const val WEBP = "image/webp" const val WEBP = "image/webp"