kotlin

image파일 저장하기

beejaem 2022. 8. 26. 12:14

사진을 촬영하면 png형식으로 내부저장소에 파일을 저장하는 코드를 짜보았다.

먼저 app에서 카메라 기능과 내부저장소를 쓰기 위한 권한을 Manifest.xml에 등록해주었다.

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera"
    android:required="true" />

그리고 layout과 버튼 생성

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.TestActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="업로드"
        android:layout_centerHorizontal="true"
        />

    <ImageView
        android:id="@+id/iv"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_centerInParent="true"
        />
</RelativeLayout>

파일을 저장하는 방식이 API29 버젼 이상과 이하로 두가지 버젼이 있다. 

//API29이상인 경우
fun saveImageOnAboveAndroidQ(bitmap: Bitmap,context: Context) {
    val fileName = "test" + System.currentTimeMillis().toString() + ".png" // 파일이름 현재시간.png
    /*
    * ContentValues() 객체 생성.
    * ContentValues는 ContentResolver가 처리할 수 있는 값을 저장해둘 목적으로 사용된다.
    * */
    val contentValues = ContentValues()

    contentValues.apply {
        put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/ImageSave") // 경로 설정
        put(MediaStore.Images.Media.DISPLAY_NAME, fileName) // 파일이름을 put해준다.
        put(MediaStore.Images.Media.MIME_TYPE, "image/png")
        put(MediaStore.Images.Media.IS_PENDING, 1) // 현재 is_pending 상태임을 만들어준다.
        // 다른 곳에서 이 데이터를 요구하면 무시하라는 의미로, 해당 저장소를 독점할 수 있다.
    }

    // 이미지를 저장할 uri를 미리 설정해놓는다.
    val uri =
        context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)

    try {
        if (uri != null) {
            val image = context.contentResolver.openFileDescriptor(uri, "w", null)
            // write 모드로 file을 open한다.

            if (image != null) {
                val fos = FileOutputStream(image.fileDescriptor)
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
                //비트맵을 FileOutputStream를 통해 compress한다.
                fos.close()

                contentValues.clear()
                contentValues.put(MediaStore.Images.Media.IS_PENDING, 0) // 저장소 독점을 해제한다.
                context.contentResolver.update(uri, contentValues, null, null)
            }
        }
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

//API29미만인 경우
fun saveImageOnUnderAndroidQ(bitmap: Bitmap,user_num : Int,context: Context) : File {
    val fileName = "$user_num.png"
    val externalStorage = Environment.getExternalStorageDirectory().absolutePath
    val path = "$externalStorage/passport"
    val dir = File(path)

    if (dir.exists().not()) {
        dir.mkdirs() // 폴더 없을경우 폴더 생성
    }

    try {
        val fileItem = File("$path/$fileName")
        fileItem.createNewFile()
        //0KB 파일 생성.

        val fos = FileOutputStream(fileItem) // 파일 아웃풋 스트림

        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
        //파일 아웃풋 스트림 객체를 통해서 Bitmap 압축.

        fos.close() // 파일 아웃풋 스트림 객체 close

        context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(fileItem)))
        // 브로드캐스트 수신자에게 파일 미디어 스캔 액션 요청. 그리고 데이터로 추가된 파일에 Uri를 넘겨준다.
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    } catch (e: Exception) {
        e.printStackTrace()
    }

    return dir
}

위의 클래스드를 이용한 전체 코드

class TestActivity : AppCompatActivity() {
    lateinit var imageView: ImageView
    lateinit var imageButton: Button
    companion object {
        private val TAG = TestActivity::class.java.simpleName
    }
    val user_num = intent.getIntExtra("user_num",0)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)

        imageView = findViewById(R.id.iv)
        imageButton = findViewById(R.id.btn)
        imageButton.setOnClickListener {
            var intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
            resultLauncher.launch(intent)
        }
    }


    private var resultLauncher = registerForActivityResult(
        StartActivityForResult()
    ) { result ->
        val data = result.data
        val img = data?.extras?.get("data") as Bitmap
        var file : File
        Log.d(TAG, data.toString())
        imageView.setImageBitmap(img)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            //Q 버전 이상일 경우. (안드로이드 10, API 29 이상일 경우)
            saveImageOnAboveAndroidQ(img, context = baseContext)

        } else {
            // Q 버전 이하일 경우. 저장소 권한을 얻어온다.
            val writePermission = ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            )

            if (writePermission == PackageManager.PERMISSION_GRANTED) {
                file = saveImageOnUnderAndroidQ(img, user_num = user_num,baseContext)
                Log.d(TAG,file.name)
            } else {
                val requestExternalStorageCode = 1

                val permissionStorage = arrayOf(
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
                )

                ActivityCompat.requestPermissions(
                    this,
                    permissionStorage,
                    requestExternalStorageCode
                )
            }
        }

    }




}

camera로 촬영한 값을 받아오는 부분의 코드도 값을 받을 수 있는 방식이 바뀌었다.

기존에 startactivityforResert메서드를 사용해 @override onActivityResult로 값을 처리했지만, 이제는 deprecated되어서 

registerForActivityResult안에 startActivityForResult를 담아서 실행시킨다. 

private var resultLauncher = registerForActivityResult(
    StartActivityForResult()
) { result ->
    val data = result.data
    val img = data?.extras?.get("data") as Bitmap
    var file: File
    Log.d(TAG, data.toString())
    imageView.setImageBitmap(img)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        //Q 버전 이상일 경우. (안드로이드 10, API 29 이상일 경우)
        saveImageOnAboveAndroidQ(img, context = baseContext)
    } else {
        // Q 버전 이하일 경우. 저장소 권한을 얻어온다.
        val writePermission = ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
        )
        if (writePermission == PackageManager.PERMISSION_GRANTED) {
            file = saveImageOnUnderAndroidQ(img, user_num = user_num, baseContext)
            Log.d(TAG, file.name)
        } else {
            val requestExternalStorageCode = 1
            val permissionStorage = arrayOf(
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            )
            ActivityCompat.requestPermissions(
                this,
                permissionStorage,
                requestExternalStorageCode
            )
        }
    }
}

관련 메서드들이 deprecated 된 부분이 많아 고생을 했다...

'kotlin' 카테고리의 다른 글

qr코드 생성하기  (0) 2022.08.22
canvas  (0) 2022.02.24
앱 만들어보기 - 1  (0) 2022.02.19
SharedPreferences  (0) 2022.02.16
handler  (0) 2022.02.10