AS-IS

PROBLEM

TO-BE

  1. File 객체를 생성하기 전, ImageDecoder 사용해 uri에 bitmap으로 접근 > 원본 이미지 크기를 알아낼 수 있음

    val source = ImageDecoder.createSource(context.contentResolver, uri)
    val bitmap = ImageDecoder.decodeBitmap(source) { decoder, **info**, _ ->
        // 디코더 설정
        decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
        decoder.isMutableRequired = true
    
        ...
    }
    

    info에 크기 정보가 담겨 있음!

  2. 원본 이미지 크기를 기반으로 자체적으로 정한 한도 내의 크기가 될 수 있도록 계산해 target 설정

    val source = ImageDecoder.createSource(context.contentResolver, uri)
    val bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
        // 디코더 설정
        decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
        decoder.isMutableRequired = true
    
        // 리사이징 크기 설정
        **val targetSize = calculateTargetSize(info.size.width, info.size.height)
        decoder.setTargetSize(targetSize.width, targetSize.height)**
    }
    
    ...
    
    private fun calculateTargetSize(
        width: Int,
        height: Int,
    ): Size {
        if (width <= MAX_WIDTH && height <= MAX_HEIGHT) return Size(width, height)
    
        val ratio = max(width.toFloat() / MAX_WIDTH, height.toFloat() / MAX_HEIGHT)
    
        return Size((width / ratio).toInt(), (height / ratio).toInt())
    }
    
    companion object {
        private const val MAX_WIDTH = 1024
        private const val MAX_HEIGHT = 1024
        private const val QUALITY = 80
    }
    

    상수 한도를 1024로 설정하고 (1024*1024 최대로 채워도 1MB 이하)

    calculateTargetSize 원본 이미지 크기와 MAX 계산해, 원본 이미지의 비율 유지하며 한도 내 크기가 되도록 목표 사이즈 계산

    decoder.setTargetSize 설정한 크기로 이미지 압축할 수 있도록 디코더에 targetSize 옵션 넣어줌

  3. 리사이징된 이미지로 File 객체 생성

    // bitmap을 리사이징(압축)한 jpeg byteArray 생성
    val compressedImage = ByteArrayOutputStream().use { stream ->
        bitmap.compress(Bitmap.CompressFormat.JPEG, QUALITY, stream)
        bitmap.recycle()
        stream.toByteArray()
    }
    
    // 임시 파일 객체 생성
    val tempFile = File(
        dir,
        "${UUID.randomUUID()}.$IMAGE_EXTENSION",
    )
    
    // 파일 객채에 리사이징 byteArray 덮어씌워 최종 이미지 파일 생성
    tempFile.outputStream().use { stream ->
        stream.write(compressedImage)
    }
    
    return tempFile
    

    이미지 파일은 JPEG로 생성하며, quality 값은 80 고정

  4. 서버 업로드 완료된다면 더 이상 파일 객체는 필요하지 않음. 따라서 삭제 로직 제공

    fun clearDirectory() {
        val directory = getDirectory()
        directory.listFiles()?.forEach { it.delete() }
    }
    

변경사항 정리

Data Layer

FileLocalDataSource 추가

위 설명한 이미지 최적화 로직은 FileLocalDataSource 가 제공합니다.