[Android, Kotlin] Multipart를 통해 서버로 이미지 전송하기

김준영·2024년 3월 2일
0

Android

목록 보기
1/17
post-thumbnail

이미지하나 때문에 정말 많은 시간이 걸렸습니다..
이미지 관련 자료들이 옛날자료들이 많고(JAVA 코드 다수) 중간에 안드로이드 보안 정책에 따라 절대경로를 통해 이미지를 얻는 방법이 막혔더라구요
작성된 글의 기능은 Q&A 관련 기능에 이미지 처리 관련 부분입니다!

1. 인터페이스 코드

interface SetQAService {
    @Multipart
    @POST("/post/{postId}/qna")
    suspend fun setQA(
        @Path("postId") postId: Int,
        @Part("QnaRequestDTO") setQARequest: SetQARequest,
        @Part image: MultipartBody.Part?
    ): Response<SetQAResponse>
}

2.임시파일 생성

object FileUtil {
    // 임시 파일 생성
    fun createTempFile(context: Context, fileName: String): File {
        val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        return File(storageDir, fileName)
    }

    // 압축 함수 추가
    fun compressAndSave(context: Context, uri: Uri, file: File) {
        val inputStream = context.contentResolver.openInputStream(uri)
        val bitmap = BitmapFactory.decodeStream(inputStream)
        val outputStream = FileOutputStream(file)

        // 압축할 품질과 함께 Bitmap을 압축하여 저장
        bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outputStream)

        outputStream.flush()
        outputStream.close()
    }
}

임시파일을 생성하는 부분과 이미지 압축에 해당하는 코드입니다. 해당 함수는 다른기능에도 쓰이기 때문에 싱글톤(Object)으로 구현했습니다.

3.Uri를 통해 이미지 얻기

object UriUtil {
    // URI -> File
    fun toFile(context: Context, uri: Uri): File {
        val fileName = getFileName(context, uri)

        val file = FileUtil.createTempFile(context, fileName)
        FileUtil.compressAndSave(context, uri, file)
        return File(file.absolutePath)
    }

    // 파일 이름 생성
    private fun getFileName(context: Context, uri: Uri): String {
        val name = uri.toString().split("/").last()
        val ext = context.contentResolver.getType(uri)!!.split("/").last()

        return "$name.$ext"
    }
}

uri를 통해 임시파일을 생성해 이미지 파일을 얻는 코드입니다. 여기서 2번에 작성된 코드를 사용합니다

4. 서버에 이미지 전송

fun setReview(context: Context, postId: Int): Flow<State<Int>> = flow{
        var imagePart: MultipartBody.Part? = null

        val request = SetReviewRequest(
            context = setWrite.content,
            score = setWrite.score
        )
		// 이미지 처리
        setWrite.imageUri?.let { uri->
            val file = UriUtil.toFile(context, uri)
            val image = file.asRequestBody("image/jpeg".toMediaTypeOrNull())
            imagePart = MultipartBody.Part.createFormData(name = "file", file.name, image)
        }
        request.toString().toRequestBody("application/json".toMediaTypeOrNull())

        val response = setReviewService.setReview(
            postId = postId,
            setReviewRequest = request,
            image = imagePart
        )
        val statusCode = response.code()

        if(statusCode == 200){
            emit(State.Success(statusCode))
        }else{
            emit(State.ServerError(statusCode))
        }
    }.catch { e->
        emit(State.Error(e))
    }

참고로 서버에서는 S3를 통해 이미지를 저장합니다!
이미지 부분은 3번코드를 활용해 uri -> 이미지를 얻습니다. 그리고 얻은 이미지를 통해 멀티파트 폼을 작성해주고 서버로 전송합니다.

profile
Android, Flutter를 공부하고 있습니다🧐

0개의 댓글

관련 채용 정보