Things take time

[Android] Uri Path -> File Path(Real Path)로 바꾸기 및 에러 발생(From Google Photo) 본문

Android(Error)

[Android] Uri Path -> File Path(Real Path)로 바꾸기 및 에러 발생(From Google Photo)

겸손할 겸 2019. 3. 5. 10:13

[현상]

 

이제 앱 자체에서 사용하는 파일 경로는 아직도 File://로 얻어낼 수 있지만, 다른 앱(앨범, 동영상, 인터넷에서 공유하기 등)에서 온 데이터는 보안상의 이슈로 Content://로 시작하는 경로로 넘어온다.

 

이 데이터들을 실제 이미지 뷰에 뿌리거나 할 경우엔 Content://를 File://의 경로로 변경하여 new File(실제 경로)로 통해 접근할 수 있다.

그런데 사용자들 중에서도 일부는 에러가 발생해서 앱이 죽는다는 것을 알아냈다.

 

나는 이제 파일 패스를 얻는 방법이 바뀌었나 하고 구글을 엄청 뒤졌으나, 별로 나오는 것이 없었다(?).

 

즉, 이건 특정상황에서만 발생하는 이슈라는 것

경로를 찍어본 결과 아래와 같다.

 

1. 에러가 안나는 경로

content://com.google.android.apps.photos.contentprovider/0/2/content%3A%2F%2Fmedia%2Fexternal%2Fvideo%2Fmedia%2F9003/ORIGINAL/NONE/1505407834

2. 에러 발생하는 경로

content://com.google.android.apps.photos.contentprovider/0/2/mediakey%3A%2Flocal%253A9e202f23-30a2-45a5-8758-0db25adca2e6/ORIGINAL/NONE/1169195434

차이점을 보자. 둘다 콘텐트 프로바이더를 통한 경로로 content URI값이지만, mediakey라는 값이 보인다.

이 경로는 구글 클라우드를 통한 경로(구글 포토)로 해석된다.

Failure delivering result ResultInfo }} 
to activity : java.lang.NullPointerException

ContentResolver로 query함수를 했을 때 해당 Uri값을 해석하는 커서가 null을 리턴한다는 것이다.

 

https://gist.github.com/alexzaitsev/75c9b36dfcffd545c676

 

이 곳에서도 볼 수있지만 중간에 mediaKey가 들어가거나, googleusercontent같은 문자열들이 들어가 있는 것은 getDataColumn()메소드에서 null값을 리턴한다는 것이다.

 

 

[해결 방법]

요약해서 설명하자면

 

하나의 파일을 Create하고, 그 Create한 파일에 Content:// 경로의 파일을 copy하고, 그 생성된 파일의 path를 리턴하는 것이다.

    public static String getFilePathFromURI(Context context, Uri contentUri) {
        String fileName = getFileName(contentUri);
        if (!TextUtils.isEmpty(fileName)) {
            File copyFile = new File(context.getFilesDir()+ File.separator + fileName);
            copy(context, contentUri, copyFile);
            return copyFile.getAbsolutePath();
        }
        return null;
    }

    public static String getFileName(Uri uri) {
        if (uri == null) return null;
        String fileName = null;
        String path = uri.getPath();
        int cut = path.lastIndexOf('/');
        if (cut != -1) {
            fileName = path.substring(cut + 1);
        }
        return fileName;
    }

    public static void copy(Context context, Uri srcUri, File dstFile) {
        try {
            InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
            if (inputStream == null) return;
            OutputStream outputStream = new FileOutputStream(dstFile);
            IOUtils.copy(inputStream, outputStream);
            inputStream.close();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

마지막 /에서 자른 String값은 기본 파일 이름 값이 되는 것이고, 이 값을 바탕으로 디렉토리 하위에 추가시킨다. 이 껍데기(copyFile)에 실제 contentUri값을 바탕으로 데이터를 inputStream으로 받아서 복사시키는 것이다. IOUtils는 아래가 기본이다.

    implementation 'commons-io:commons-io:2.4'

이렇게하면 이미지는 관련 return null은 해결된다.

 

 

 

그러나, Photo가 아닌 Video는 어떻게 해결하는가.

 

카카오톡에서는 어떻게 하나 봤더니, 일반 동영상은 바로 불러와 getDataColumn()이 잘 수행되어 빠르게 불러오는데 저 오류가 나는 동영상(클라우드)은 시간이 오래걸렸다. 아무래도 또 파일을 생성후 불러오는 작업을 수행하는 것 같다.

 

그러므로 나는 직접 구현하느니, 라이브러리를 사용하기로 했다. (!)

 

 

 

어쨌든, 이미지 파일(웹에서 온)이나 앨범에서 특정 Image파일에 대한 Null Return 에러는 해결됐다.

 

 

[20210405]

(댓글 참고) :: 파일에 Uri값을 지정하여 생성/조작하는게 AOS11이상부터는 막혔다고 한다. 이전 버전 호환성에 대해서는 위 처리로직을 사용해도 되지만, 이후는 최신 자료를 찾아보는게 좋을 것 같다.