Skip to content

Commit

Permalink
[FIX/#47] ContentUriRequestBody 적용
Browse files Browse the repository at this point in the history
  • Loading branch information
Marchbreeze committed Jun 4, 2024
1 parent d894052 commit edc5b05
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package kr.genti.data.repositoryImpl

import android.content.Context
import androidx.core.net.toUri
import android.net.Uri
import dagger.hilt.android.qualifiers.ApplicationContext
import kr.genti.data.service.UploadService
import kr.genti.data.util.ContentUriRequestBody
import kr.genti.domain.repository.UploadRepository
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import javax.inject.Inject

class UploadRepositoryImpl
Expand All @@ -18,17 +17,11 @@ class UploadRepositoryImpl
override suspend fun uploadImage(
preSignedURL: String,
imageUri: String,
): Result<Int?> =
): Result<Unit> =
runCatching {
uploadService.putS3Image(
preSignedURL,
context.contentResolver.openInputStream(
imageUri.toUri(),
)?.readBytes()?.toRequestBody(OCTET_STREAM.toMediaType()),
ContentUriRequestBody(context, Uri.parse(imageUri)),
)
}

companion object {
private const val OCTET_STREAM = "application/octet-stream"
}
}
2 changes: 1 addition & 1 deletion data/src/main/java/kr/genti/data/service/UploadService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ interface UploadService {
suspend fun putS3Image(
@Url preSignedURL: String,
@Body image: RequestBody?,
): Int?
)
}
144 changes: 144 additions & 0 deletions data/src/main/java/kr/genti/data/util/ContentUriRequestBody.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package kr.genti.data.util

/**
* sopt-makers/sopt-android
* @author chattymin
*/

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.media.ExifInterface
import android.net.Uri
import android.provider.MediaStore
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
import okio.BufferedSink
import java.io.ByteArrayOutputStream

class ContentUriRequestBody(
context: Context,
private val uri: Uri?,
) : RequestBody() {
private val contentResolver = context.contentResolver

private var size = -1L
private var compressedImage: ByteArray? = null

init {
if (uri != null) {
contentResolver.query(
uri,
arrayOf(MediaStore.Images.Media.SIZE, MediaStore.Images.Media.DISPLAY_NAME),
null,
null,
null,
)?.use { cursor ->
if (cursor.moveToFirst()) {
size =
cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE))
}
}

compressBitmap()
}
}

private fun compressBitmap() {
if (uri != null) {
var originalBitmap: Bitmap
val exif: ExifInterface

contentResolver.openInputStream(uri).use { inputStream ->
if (inputStream == null) return
val option =
BitmapFactory.Options().apply {
inSampleSize = calculateInSampleSize(this, MAX_WIDTH, MAX_HEIGHT)
}
originalBitmap = BitmapFactory.decodeStream(inputStream, null, option) ?: return
exif = ExifInterface(inputStream)
}

var orientation =
exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL,
)
when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> orientation = 90
ExifInterface.ORIENTATION_ROTATE_180 -> orientation = 180
ExifInterface.ORIENTATION_ROTATE_270 -> orientation = 270
}

if (orientation >= 90) {
val matrix =
Matrix().apply {
setRotate(orientation.toFloat())
}

val rotatedBitmap =
Bitmap.createBitmap(
originalBitmap,
0,
0,
originalBitmap.width,
originalBitmap.height,
matrix,
true,
)
originalBitmap.recycle()
originalBitmap = rotatedBitmap
}

val outputStream = ByteArrayOutputStream()
val imageSizeMb = size / (MAX_WIDTH * MAX_HEIGHT.toDouble())
outputStream.use {
val compressRate = ((3 / imageSizeMb) * 100).toInt()
originalBitmap.compress(
Bitmap.CompressFormat.JPEG,
if (imageSizeMb >= 3) compressRate else 100,
it,
)
}
compressedImage = outputStream.toByteArray()
size = compressedImage?.size?.toLong() ?: -1L
}
}

private fun calculateInSampleSize(
options: BitmapFactory.Options,
reqWidth: Int,
reqHeight: Int,
): Int {
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1

if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = width / 2

while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}

return inSampleSize
}

override fun contentLength(): Long = size

// override fun contentType(): MediaType? = OCTET_STREAM.toMediaTypeOrNull()
override fun contentType(): MediaType? = uri?.let { contentResolver.getType(it)?.toMediaTypeOrNull() }

override fun writeTo(sink: BufferedSink) {
compressedImage?.let(sink::write)
}

companion object {
const val MAX_WIDTH = 1024
const val MAX_HEIGHT = 1024
// private const val OCTET_STREAM = "application/octet-stream"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ interface UploadRepository {
suspend fun uploadImage(
preSignedURL: String,
imageUri: String,
): Result<Int?>
): Result<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,9 @@ class CreateViewModel

fun getS3PresignedUrls() {
// TODO: 파일명 대응
// TODO: USER_UPLOAD_IMAGE 로 파일 타입 변경
if (plusImage != Uri.EMPTY) {
viewModelScope.launch {
createRepository.getS3SingleUrl(S3RequestModel("sangho1.png", FileType.USER_UPLOADED_IMAGE))
createRepository.getS3SingleUrl(S3RequestModel("sangho1.jpg", FileType.USER_UPLOADED_IMAGE))
.onSuccess { uriModel ->
_getS3UrlResult.emit(true)
postSingleImage(uriModel)
Expand All @@ -130,9 +129,9 @@ class CreateViewModel
viewModelScope.launch {
createRepository.getS3MultiUrl(
listOf(
S3RequestModel("sangho2.png", FileType.USER_UPLOADED_IMAGE),
S3RequestModel("sangho3.png", FileType.USER_UPLOADED_IMAGE),
S3RequestModel("sangho4.png", FileType.USER_UPLOADED_IMAGE),
S3RequestModel("sangho2.jpg", FileType.USER_UPLOADED_IMAGE),
S3RequestModel("sangho3.jpg", FileType.USER_UPLOADED_IMAGE),
S3RequestModel("sangho4.jpg", FileType.USER_UPLOADED_IMAGE),
),
).onSuccess { uriList ->
_getS3UrlResult.emit(true)
Expand Down

0 comments on commit edc5b05

Please sign in to comment.