android: compress images before media upload
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package ru.daemonlord.messenger.data.media.repository
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
@@ -15,6 +17,8 @@ import ru.daemonlord.messenger.di.RefreshClient
|
||||
import ru.daemonlord.messenger.domain.common.AppError
|
||||
import ru.daemonlord.messenger.domain.common.AppResult
|
||||
import ru.daemonlord.messenger.domain.media.repository.MediaRepository
|
||||
import java.io.ByteArrayOutputStream
|
||||
import kotlin.math.roundToInt
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -32,15 +36,20 @@ class NetworkMediaRepository @Inject constructor(
|
||||
bytes: ByteArray,
|
||||
): AppResult<Unit> = withContext(ioDispatcher) {
|
||||
try {
|
||||
val uploadPayload = prepareUploadPayload(
|
||||
fileName = fileName,
|
||||
mimeType = mimeType,
|
||||
bytes = bytes,
|
||||
)
|
||||
val uploadInfo = mediaApiService.requestUploadUrl(
|
||||
request = UploadUrlRequestDto(
|
||||
fileName = fileName,
|
||||
fileType = mimeType,
|
||||
fileSize = bytes.size.toLong(),
|
||||
fileName = uploadPayload.fileName,
|
||||
fileType = uploadPayload.mimeType,
|
||||
fileSize = uploadPayload.bytes.size.toLong(),
|
||||
)
|
||||
)
|
||||
|
||||
val body = bytes.toRequestBody(mimeType.toMediaTypeOrNull())
|
||||
val body = uploadPayload.bytes.toRequestBody(uploadPayload.mimeType.toMediaTypeOrNull())
|
||||
val uploadRequestBuilder = Request.Builder()
|
||||
.url(uploadInfo.uploadUrl)
|
||||
.put(body)
|
||||
@@ -61,8 +70,8 @@ class NetworkMediaRepository @Inject constructor(
|
||||
request = AttachmentCreateRequestDto(
|
||||
messageId = messageId,
|
||||
fileUrl = uploadInfo.fileUrl,
|
||||
fileType = mimeType,
|
||||
fileSize = bytes.size.toLong(),
|
||||
fileType = uploadPayload.mimeType,
|
||||
fileSize = uploadPayload.bytes.size.toLong(),
|
||||
)
|
||||
)
|
||||
AppResult.Success(Unit)
|
||||
@@ -70,4 +79,58 @@ class NetworkMediaRepository @Inject constructor(
|
||||
AppResult.Error(error.toAppError())
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareUploadPayload(
|
||||
fileName: String,
|
||||
mimeType: String,
|
||||
bytes: ByteArray,
|
||||
): UploadPayload {
|
||||
if (!mimeType.startsWith("image/", ignoreCase = true)) {
|
||||
return UploadPayload(fileName = fileName, mimeType = mimeType, bytes = bytes)
|
||||
}
|
||||
if (mimeType.equals("image/gif", ignoreCase = true)) {
|
||||
return UploadPayload(fileName = fileName, mimeType = mimeType, bytes = bytes)
|
||||
}
|
||||
val source = runCatching { BitmapFactory.decodeByteArray(bytes, 0, bytes.size) }.getOrNull()
|
||||
?: return UploadPayload(fileName = fileName, mimeType = mimeType, bytes = bytes)
|
||||
val maxSide = 1920
|
||||
val width = source.width
|
||||
val height = source.height
|
||||
val scale = (maxSide.toFloat() / maxOf(width, height).toFloat()).coerceAtMost(1f)
|
||||
val targetWidth = (width * scale).roundToInt().coerceAtLeast(1)
|
||||
val targetHeight = (height * scale).roundToInt().coerceAtLeast(1)
|
||||
|
||||
val scaled = if (targetWidth != width || targetHeight != height) {
|
||||
Bitmap.createScaledBitmap(source, targetWidth, targetHeight, true)
|
||||
} else {
|
||||
source
|
||||
}
|
||||
val output = ByteArrayOutputStream()
|
||||
val compressedOk = runCatching {
|
||||
scaled.compress(Bitmap.CompressFormat.JPEG, 82, output)
|
||||
}.getOrDefault(false)
|
||||
if (scaled !== source) {
|
||||
scaled.recycle()
|
||||
}
|
||||
source.recycle()
|
||||
if (!compressedOk) {
|
||||
return UploadPayload(fileName = fileName, mimeType = mimeType, bytes = bytes)
|
||||
}
|
||||
val compressedBytes = output.toByteArray()
|
||||
if (compressedBytes.size >= bytes.size) {
|
||||
return UploadPayload(fileName = fileName, mimeType = mimeType, bytes = bytes)
|
||||
}
|
||||
val baseName = fileName.substringBeforeLast('.').ifBlank { "image" }
|
||||
return UploadPayload(
|
||||
fileName = "$baseName-web.jpg",
|
||||
mimeType = "image/jpeg",
|
||||
bytes = compressedBytes,
|
||||
)
|
||||
}
|
||||
|
||||
private data class UploadPayload(
|
||||
val fileName: String,
|
||||
val mimeType: String,
|
||||
val bytes: ByteArray,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user