본문 바로가기

Android/Function

[안드로이드] 외부저장소(SAF) ↔ 내부저장소 파일 옮기기/복사하기

728x90
반응형

 

 

SAF(Scoped Access Framework)를 이용한 파일 쓰기를 대비해야하는데요 ^^

내부저장소에 저장된 파일을 SAF를 이용하여 외부저장소에 복사, 혹은 그 반대의 예제 입니다.

SAF 파일 쓰기는 밑의 포스트를 참조해 주세요.

[Android/Function] - [안드로이드] 외부 저장소에 데이터 파일 저장하기(쓰기) ― SAF(Storage Access Framework)로 파일 쓰기

 

내부저장소 → 외부저장소(SAF)


 

  • SAF 열기

   /**
     * Export
     */
     
    private val REQUEST_WRITE = 43

    fun onClickCopy(view: View){
        saf()
    }


    fun saf() {
        try {
            /**
             * SAF 파일 편집
             */
            val fileName = //... 파일명
            val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply{
                addCategory(Intent.CATEGORY_OPENABLE)
                type = "*/*"
                putExtra(Intent.EXTRA_TITLE, fileName)
            }

            startActivityForResult(intent, REQUEST_WRITE)
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when (requestCode) {
            REQUEST_WRITE -> if (resultCode == RESULT_OK && data != null) {
                writeFile(data)
            }
            else -> super.onActivityResult(requestCode, resultCode, data)
        }
    }

버튼이 눌리면 SAF를 이용해 파일을 만듭니다.

파일 저장 후 onActivityResult에서 결과를 받아 파일 쓰기를 시작합니다.

 

  • 파일 쓰기(복사)

    //내부 저장소 경로
    private var mOutputDir = MyApplication.applicationContext().getExternalFilesDir(null)
    private var pfd: ParcelFileDescriptor? = null
    private var fileOutputStream: FileOutputStream? = null
    
    fun writeFile(data: Intent?) {

        val uri = data?.data
        try {
            pfd = uri?.let { mApplication.contentResolver.openFileDescriptor(it, "w") }
            fileOutputStream = FileOutputStream(pfd?.fileDescriptor)
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        }
        // 내부저장소 파일 불러오기
        var src: File? = null
        if(fileName!=null) { //내부저장소 파일 이름
            src = File(mOutputDir, fileName!!)
        }

        var inChannel: FileChannel? = null
        var outChannel: FileChannel? = null

        try {
            inChannel = FileInputStream(src).channel
            outChannel = fileOutputStream?.channel
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        }

        try {
            inChannel?.transferTo(0, inChannel.size(), outChannel)
        } finally {
            inChannel?.close()
            outChannel?.close()
            //Util.showNotification("저장되었습니다.")
            fileOutputStream?.close();
            pfd?.close();
        }
    }

 

외부저장소(SAF) → 내부저장소


 

  • SAF 열기
fun saf() {
     val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "*/*"
      }
      startActivityForResult(intent, READ_REQUEST_CODE)
}

saf() 메서드를 통해 외부저장소에서 불러올 파일을 선택합니다.

 

  • 파일 쓰기(복사)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
     when (requestCode) {
          READ_REQUEST_CODE -> if (resultCode == AppCompatActivity.RESULT_OK && data != null) {
              data.data?.also { uri ->
                    saveFile(uri)
              }
          }
          else -> super.onActivityResult(requestCode, resultCode, data)
      }
}

onActivityResult에서 받아온 uri를 이용해 파일 쓰기를 시작합니다.

 

    private var pfd: ParcelFileDescriptor? = null
    private var fileInputStream: FileInputStream? = null
    private var mOutputDir = MyApplication.applicationContext().getExternalFilesDir(null)
    fun saveFile(uri: Uri){
        val fileName = getFileName(uri)
        try {
            pfd = uri.let { mApplicationContext?.contentResolver?.openFileDescriptor(it, "r") }
            fileInputStream = FileInputStream(pfd?.fileDescriptor)
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        }
        var newFile: File? = null
        if(fileName!=null) {
            newFile = File(mOutputDir, fileName)
        }

        var inChannel: FileChannel? = null
        var outChannel: FileChannel? = null

        try {
            inChannel = fileInputStream?.channel
            outChannel = FileOutputStream(newFile).channel
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        }

        try {
            inChannel?.transferTo(0, inChannel.size(), outChannel)
        } finally {
            inChannel?.close()
            outChannel?.close()
            Util.showNotification("성공적으로 불러왔습니다.")
            fileInputStream?.close()
            pfd?.close()
        }
    }

    fun getFileName(uri: Uri): String? {
        var result: String? = null
        if (uri.scheme == "content") {
            val cursor: Cursor? = mApplicationContext?.contentResolver?.query(uri, null, null, null, null)
            try {
                if (cursor != null && cursor.moveToFirst()) {
                    result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
                }
            } finally {
                cursor?.close()
            }
        }
        if (result == null) {
            result = uri.path
            val cut = result!!.lastIndexOf('/')
            if (cut != -1) {
                result = result.substring(cut + 1)
            }
        }
        return result
    }

 

 

 

  • 참고 자료

Copy file from the internal to the external storage

 

 

 

728x90
반응형