오늘도 삽질중

Android SAF 파일,폴더 생성 예제 with. DocumentFile 본문

안드로이드

Android SAF 파일,폴더 생성 예제 with. DocumentFile

Choi3950 2020. 12. 10. 18:25
반응형


Http URL로 파일 업로드를 구현해야 하는 일이 있어서 SAF 로 파일생성이나 폴더생성 예제를 찾아보았지만 제가 원하는 예제는 존재하지 않았습니다.

그래서 파일 및 폴더 생성 부분만 간단하게 직접 테스트해보고 구현해봤습니다.

내부저장소에 폴더 및 파일을 생성하게 됩니다.


해당 소스로 Android OS 7.0도 문제 없는걸로 보아 하위버전까지 호환이 가능한거 같습니다.


앱내 저장소는 FilePath 를 사용하여 작업이 가능하나, 저장소 정책이 변경되어 FilePath를 사용하는 코드는 모든 상황에 대응하기 힘들다고 판단하였습니다.

해당 로직의 핵심은 FilePath를 사용하지 않습니다.

오로지 uri로 모든 작업을 처리하였습니다.



해당 유튜브 영상과 블로그가 도움이 많이 되었습니다. 참고해보시면 좋을꺼 같습니다.

https://www.youtube.com/watch?v=LNjRF4YcGlQ

https://www.programmersought.com/article/14973536308/



혹시나 카메라나 앨범예제가 필요하신분은 SAF로 대응하여 카메라 및 앨범예제를 구현해보았습니다.

핵심로직은 앱 캐시저장공간에 캐시파일을 만들고 파일디스크립터를 사용하여 이미지 처리 및 앨범에 저장하는 식으로 구현해봤습니다.

https://choi3950.tistory.com/20




선행작업

AppLevel Gradle 추가

implementation "androidx.documentfile:documentfile:1.0.1"



1. 폴더를 생성하고 그 안에 파일도 생성하는 로직.


Intent 호출

findViewById<Button>(R.id.btn).setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent , 100)
}

해당 인텐트 호출시 기종, 버전마다 Select Ui가 다르게 보이게 됩니다.

다이렉트로 FilePath를 가져오는것이 안되니 사용자가 직접 어디다 폴더를 생성하고, 파일을 생성할 것인지 선택을 해야합니다.

사용성 측면에선 조금 불편할지 모르겠지만 , 저장소 정책이 변경된 만큼 이런 Ui를 자주 접하게 되면 사용자도 익숙해지지 않을까요? ㅎㅎ

해당 인텐트를 호출하면 이런식으로 Select Ui가 호출됩니다.


폴더가 나오지 않고 흰색 화면만 나오는 경우도 있는데, 상단에 네비게이션바를 클릭하여 폴더를 바꾸면 보실수 있습니다.

그리고 허용하는 버튼도 Ui가 각각 다릅니다. 이전에 사용하던 Note5 는 "선택" 이라고 2글자로만 Ui가 되있더군요.




onActivityResult

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == 100) {


val uri = data?.data
//uri ex) content://com.android.externalstorage.documents/tree/primary%3A

uri?.let {

//Uri 에 대한 접근 권한허용
val takeFlags = (intent.flags
and (Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION))
contentResolver.takePersistableUriPermission(it, takeFlags)


val folderDoc = DocumentFile.fromTreeUri(this, it)
folderDoc
?.createDirectory("NewDir")
?.createFile("text/plain", "TestFile.txt")
?.also {
contentResolver.openOutputStream(it.uri)?.write("Test Input TXT".toByteArray())
}
}


} }



해당 코드를 실행하시면 내부저장소에 NewDir 폴더와 그 안에 TestFile.txt 가 생성됩니다.


해당 텍스트파일을 클릭하면 파일을 실행할수 있는 Select Ui가 나오며 파일내용 확인이 가능합니다.




2. 폴더 및 파일존재 여부 확인하고 폴더가 존재하지 않으면 폴더 생성 및 파일 생성

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == 100) {

val uri = data?.data
//uri ex) content://com.android.externalstorage.documents/tree/primary%3A

uri?.let {

//Uri 에 대한 접근 권한허용
val takeFlags = (intent.flags
and (Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION))
contentResolver.takePersistableUriPermission(it, takeFlags)


val folderDoc = DocumentFile.fromTreeUri(this, it)
folderDoc?.let { doc ->
val folder = doc.findFile("NewDir")
if (folder?.exists() == true) { //폴더 존재여부 확인
Toast.makeText(this, "디렉토리가 존재합니다.", Toast.LENGTH_SHORT).show()


//폴더가 존재하니 그 안에 파일도 존재하는지 확인
val fileDoc = DocumentFile.fromTreeUri(this, folder.uri)
val file = fileDoc?.findFile("TestFile.txt")
if (file?.exists() == true) {
Toast.makeText(this, "파일도 존재합니다.", Toast.LENGTH_SHORT).show()
}
return

} else { //폴더가 존재하지 않으니 폴더 생성 및 파일생성

doc.createDirectory("NewDir")
?.createFile("text/plain", "TestFile.txt")
?.also {
contentResolver.openOutputStream(it.uri)
?.write("Test Input GO".toByteArray())
}
}

}

}


} }

uri을 받아와 폴더존재여부를 확인합니다. 폴더가 존재하면 파일 존재여부도 확인하고, 폴더가 없다면 폴더와 파일을 생성합니다.

해당 코드를 잘 보시면 DocumentFile.fromTreeUri() 과 , findFile() 함수를 사용하고 있습니다.


여기서 헷갈릴 여지가 있을 수 있는게 폴더도 결국은 파일로 취급됩니다. 즉 디렉토리라 부르지만 결국 이것도 파일이죠.


그 외에는 DocumentFile 문서를 보시면서 작업하시길 추천드립니다.

https://developer.android.com/reference/androidx/documentfile/provider/DocumentFile





해당 소스는 제가 맨 위에 스크린샷으로 올린 Select Ui 를 뭘 선택하느냐에 생성되는 위치가 달라집니다.

예를들어 처음에 Select Ui에서 그대로 허용을 했다면 내부저장소 -> NewDir -> TestFile.txt 가 생성되지만,

Select Ui에서 예를들어 Download 폴더에 진입후에 허용 한다면

내부저장소 -> Download -> NewDir -> TestFile.txt 로 생성이 됩니다.

즉 유저가 파일을 어디다 생성할지 직접 정하기 때문에 이건 어쩔수 없다고 생각합니다.


onActivityResult 에서 넘어온 uri가 현재 내가 허용한 디렉토리의 uri임을 확실히 아셔야 합니다.

그렇지 않으면 작업하면서도 매우 헷갈리는 상황이 오게 될꺼 같습니다.



그 외 만약 폴더와 파일이 모두 존재한다면 바로 파일을 오픈해주는 기능과 , Http URL로 업로드를 하여 처리하는 로직은 아직 구현중에 있습니다.

추후 구현이 된다면 별도로 포스팅을 진행해보겠습니다.



해당 예제가 구현상 꼭 필요한 분이 있을꺼라 생각합니다.

아직 부족한 실력이지만 누군가에게 꼭 도움이 되었으면 좋겠습니다.


반응형
Comments