일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 스위프트
- 안드로이드 FCM
- flutter 회전
- swift autolayout
- 앱 백그라운드 푸시 데이터 저장
- NotificationService Extension
- 안드로이드 숏컷
- 스위프트 앨범
- 안드로이드 에러
- 안드로이드 바로가기
- 스위프트 UserDefaults
- 앱 꺼졌을 때 푸시 데이터 저장
- FlutterView MethodChannel
- Flutter NativeView
- 스위프트 푸시
- native flutter view
- 플러터 뷰 컨트롤러
- 스위프트 카메라
- 노티피케이션 익스텐션
- swift sms
- 안드로이드 앨범
- silent push
- Swift flutterviewcontroller
- swift 문자
- Flutter UIKitView MethodChannel
- Swift flutterview
- 푸시 데이터 저장
- flutter rotate
- 스위프트 웹뷰
- 스위프트 테이블 뷰 셀
- Today
- Total
Things take time
[Android] Uri Path -> File Path(Real Path)로 바꾸기 및 에러 발생(From Google Photo) 본문
[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이상부터는 막혔다고 한다. 이전 버전 호환성에 대해서는 위 처리로직을 사용해도 되지만, 이후는 최신 자료를 찾아보는게 좋을 것 같다.