본문 바로가기

개발 코딩 정보 공유/안드로이드 자바 코틀린

웹뷰뿌시기 (webview) 파일업로드(사진업로드)

 

 

 

 

언제쯤 휴가를 갈수 있을까...?

 

 

 

안녕하세요. 김과자 입니다.

오늘은 1편에 이어서 예정대로 웹뷰 뿌시기 2편

웹뷰계의 최강 빌런 !!! 파일업로드...에 대해서 알아보겠습니다.

웹뷰에서 파일업로드를 처리하기 위해서는 소스에서 html5 이용해서 

form 태그에서 input type file 지정하기만 하면 뒤는 

식은죽 ................

 

 

 

 

"여러분. 안드로이드 개발에 그런일은 없습니다."

의심하지 마세요. 모든것이 수동입니다.”

 

 

 

 

그리하여 웹뷰에서 파일업로드를 처리하기위해서는

약 3가지 OS 버전에 대해 고려해야합니다.

왜냐구요? 버전별로 구현방식이 다릅니다. 

 

 

 

 

"네???"

 

 

 

 

 

하하하하ㅏㅎㅎㅎ하ㅏㅎ하ㅏ하ㅏ하...

 

 

 

 


 

 

 

웹뷰 뿌시기 2편 - 파일업로드(혹은 사진업로드)

시작해보겠습니다. 안드로이드 웹뷰상에서 파일(사진)업로드를 처리하기 위해서는 

아래의 os 버전을 고려해야 합니다. 

-롤리팝이상(5.0이상)
-킷캣만(4.x)
-킷캣이하(4.x이하)

 

구현에 다소 난해한 부분도 있구요. 어려울수도 있지만.

최대한 흐름 위주로 개념을 알고 계시면 응용이 가능하오니...

그럼 너무 겁내지마시고... ㄱ ㄱ ㄱ

 

 


 

 

 

롤리팝이상(API 5.0+)

 

요즘은 대부분의 최소기준이 무난하게 롤리팝까지 올라왔기 때문에 (아니면 그냥 지금이라도 도망가세...ㅇ)

왠만하면 방법정도만 알고있어도 파일업로드 구현에 문제가 없습니다.

동작의 대략적인 플로우는 이렇습니다.

 

 

1. 웹소스

웹소스(웹뷰상에 띄울페이지)에서 input type = “file” 구현합니다. 

<html>
<body>
…
<form enctype=“multipart/form-data”>
<input type="file" value="파일첨부" accept="image/*"/>
</form>
…
</html>

 

 

2.[웹뷰:안드로이드웹뷰소스]

웹뷰에서 해당동작을 컨트롤하기위해서는 'onShowFileChooser'  구현이 필요합니다.

주의하실점은 os 버전별로 해당 메서드가 다른게 구성되어있습니다.

아래의 구현은 5.0+ 기준입니다.

//Webview의 chrome client 내부에서 작업

var uploadMessage: ValueCallback<Array<Uri?>>? = null //이미지 업로드시 사용
…

//For Android 5.0+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback<Array<Uri?>>,
                               fileChooserParams: WebChromeClient.FileChooserParams): Boolean {

    //일단은 image 를 업로드한다는 가정에서 작업.
    val acceptType = fileChooserParams.acceptTypes[0].toString() //access 타입을 체크합니다.

    //filePathCallback: ValueCallback<Array<Uri?> 이놈의 경우 한번 오픈했으면 반드시 초기화 해줘야합니다.
    //확인또확인!
    //안 그러면 두번다시 안열리거나 에러를 뱉습니다.
    if (uploadMessage != null) {
        uploadMessage!!.onReceiveValue(null)
    }
    uploadMessage = filePathCallback

    if(context.getCheckPermission(
            arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA))) { //권한이 있다면
        
//예제는 파일chooser를 이용해서 파일처리해보겠습니다.
val contentSelectionIntent = Intent() //Intent.ACTION_GET_CONTENT
//if (mode == FILE_MULTIPLE_SELECT){ //멀티플 처리한다면
//contentSelectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
//}
contentSelectionIntent.action = Intent.ACTION_GET_CONTENT
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE)
contentSelectionIntent.type = "image/*"
startActivityForResultFromAction(contentSelectionIntent, REQUEST_FILE_UPLOAD)

    }else{

       //… 권한이 없을경우 작업 처리
       //…

        //얘는 무조건 해줘야 합니다!!!
        if (uploadMessage != null) {
            uploadMessage!!.onReceiveValue(null)
        }
        uploadMessage = null

    }
    return true
}

 

 

3.[액티비티에서처리]

'file chooser' 'document 탐색기' 등으로 파일(사진)선택후 'Activity'의 'onActivityResult' 에서 처리합니다.

예를 들어 아래와 같은 소스처리를 하면되겠습니다.

//Activity의 onActivityResult에서 처리한다.

…
//request code 처리
REQUEST_FILE_UPLOAD ->{ //파일 업로드 관련 (파일 업로드 -> 파일탐색기 처리시)

    if (Build.VERSION.SDK_INT >= 21) {
        var results: Array<Uri?>? = null

        if (resultCode == Activity.RESULT_OK) {
            try{

                if (webview.mUploadMessage == null) {
                    return
                }
                
                if (intent != null) {
                    val dataString = intent.dataString
                    val clipData = intent.clipData

                    //멀티선택
                    /*if (clipData != null) {
                        results = arrayOfNulls(clipData.itemCount)
                        for (i in 0 until clipData.itemCount) {
                            val item = clipData.getItemAt(i)
                            results[i] = item.uri
                        }
                    }*/

                    //단일선택
                    if (dataString != null) {
                        results = arrayOf(Uri.parse(dataString))
                    }

                }
                

                webview.mUploadMessage.onReceiveValue(results)
                webview.mUploadMessage = null
            }catch(e:Exception){
                Log.i(TAG, e.message)
                Toast.makeText(this, "업로드실패 ", Toast.LENGTH_SHORT).show()
            }
        }else{ //cancel 등등처리 
          webview.mUploadMessage.onReceiveValue(null)
          webview.mUploadMessage = null

        }

    }
}

 

여기까지가 롤리팝에서 파일업로드 작업의 흐름입니다.

간단하게 예를 들어 작성하였습니다. 

input type file -> onShowFileChooser -> Activity(onActivityResult)처리의 흐름을 알고 계시면

구현 방식은 입맛에 맞게 응용해서 처리하시면 되겠습니다여기까지는 나름 수월하다고 볼수있죠?

다음부터가 문제입니다. 앱의 최소버전이 키캣까지 내려갈경우골치아파집니다.

 

 

 

 

"왜냐?"

 

 

 

 

킷캣 버전의 웹뷰에서는 해당 'onShowFileChooser' 메서드를 지원하지 않습니다.

디플리케이트 되었냐구요? 아뇨. 없어요. 그런거. 아에 지원 안함요.

 

 

 

 

하하핳하핳하하핳하ㅏㅎㅎ핳핳

 

 

 

 

 


 

 

 

 

 

☆ 참고

 

시간이 되신다면 제가 리서치 했던 아래 내용들도 참고 해보시면 더욱 좋겠네요.

(내용은... 번역기로 돌렸...)

Q : 내 웹보기에서 "파일 선택" 버튼을 클릭하여 Samsung 7 인치 태블릿에서 파일 브라우저를 열고 싶지만 
Android 버전 4.4.2 에서 열 수 없습니다 . 그래서 이것을 도울 수 있습니까? 여기 내 코드가 있습니다.

A : 불행히도 openFileChooser는 공개 API가 아닙니다. 
Google은 향후 Android 릴리스에서 공개 API를 개발 중입니다.
이는 KitKat에서 수행 할 수 없으며 수많은 개발자가 좌절하는 부분이다.
같은 문제가 있습니다. KitKat 웹뷰에서 API openFileChooser가 제거되었습니다.
불행히도 이것은 잘 알려진 문제입니다. Google은 의도적으로 4.2에서 비활성화했으며 4.4에서 여전히 깨졌습니다.

 

출처 : 

https://issuetracker.google.com/issues/36983532

https://stackoverflow.com/questions/28600165/cant-open-file-chooser-in-webview-android-4-4-2-using-webchromeclient

https://stackoverflow.com/questions/26101065/choose-file-to-upload-on-webview-android-kitkat-4-4-2

 

 

 

 


 

 

 

 

그렇다면? 어떻게 해야하나요?”

제일 좋은 방법은 4.4 지원하지 마세요. 제발 킷캣좀 놓아주세요.

부탁드립니다.

ㅋㅋㅋㅋㅋㅋ

 

작업중에 제일 어이없는건4.4 킷캣  지원안한다는겁니다.

오히려 이하 버전들에서는 'onShowFileChooser' 에서 처리할수 있습니다.(방식은 조금 다릅니다만.) 

 

.. 다시 돌아와서. 그래도 지원해야겠다면일이 커지므로

다음편에 계속하겠습니다... 

미리 이야기 해보자면 해당 파일 업로드를 처리하기 위한 대략적인 흐름은 이렇습니다.

 

 

 

 

- 4.4 킷캣을 위한 업로드 방법(흐름)

 

1. 앱(네이티브 작업)

 

1. 웹소스 에서 js 를 이용하여 native단 메서드를 call 한다.
2. js interface 메서드에서 이미지인지 카메라인지 파라미터 확인후
3. 카메라를 열든, 파일선택기를 열든 처리한후
4. Activity의 onActivityResult에서
5. 해당 파일(사진)을 native 상의 http 통신을 작성하여
6. multipart(post) 로 해당 파일을 서버로 (바이너리) 쏴준다.

 

 

2. 서버작업

들어온 해당 파일업로드 request 를 받아서 처리한다.

 

 

 

이것들이 대략적인 흐름이구요. 다음시간에 구현 예를 들어보겠습니다.

과정은 'node.js', 'RXJava', 'Retrofit'  활용해서 예를 들어보겠습니다.

 

 

 

그럼 개발자 님들 화이팅하세요...ㅠ

즐코딩!