이미지하나 때문에 정말 많은 시간이 걸렸습니다..
이미지 관련 자료들이 옛날자료들이 많고(JAVA 코드 다수) 중간에 안드로이드 보안 정책에 따라 절대경로를 통해 이미지를 얻는 방법이 막혔더라구요
작성된 글의 기능은 Q&A 관련 기능에 이미지 처리 관련 부분입니다!
interface SetQAService {
@Multipart
@POST("/post/{postId}/qna")
suspend fun setQA(
@Path("postId") postId: Int,
@Part("QnaRequestDTO") setQARequest: SetQARequest,
@Part image: MultipartBody.Part?
): Response<SetQAResponse>
}
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)으로 구현했습니다.
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번에 작성된 코드를 사용합니다
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 -> 이미지를 얻습니다. 그리고 얻은 이미지를 통해 멀티파트 폼을 작성해주고 서버로 전송합니다.