Android : 콘텐츠 URI에서 파일 URI를 가져 오시겠습니까?


133

내 응용 프로그램에서 사용자는 응용 프로그램이 처리하는 오디오 파일을 선택해야합니다. 문제는 앱이 오디오 파일과 관련하여 원하는 작업을 수행하려면 URI가 파일 형식이어야한다는 것입니다. Android의 기본 뮤직 플레이어를 사용하여 앱에서 오디오 파일을 탐색하면 URI는 콘텐츠 URI이며 다음과 같습니다.

content://media/external/audio/media/710

그러나 인기있는 파일 관리자 응용 프로그램 인 Astro를 사용하면 다음과 같은 결과가 나타납니다.

file:///sdcard/media/audio/ringtones/GetupGetOut.mp3

후자는 작업하기가 훨씬 쉬워졌지만 물론 앱이 컬렉션을 탐색하는 데 사용하는 프로그램에 관계없이 사용자가 선택한 오디오 파일과 기능을 갖기를 원합니다. 내 질문은 content://스타일 URI를 URI로 변환하는 방법이 file://있습니까? 그렇지 않으면이 문제를 해결하기 위해 무엇을 추천 하시겠습니까? 선택기를 호출하는 코드는 다음과 같습니다.

Intent ringIntent = new Intent();
ringIntent.setType("audio/mp3");
ringIntent.setAction(Intent.ACTION_GET_CONTENT);
ringIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(ringIntent, "Select Ringtone"), SELECT_RINGTONE);

콘텐츠 URI로 다음을 수행합니다.

m_ringerPath = m_ringtoneUri.getPath();
File file = new File(m_ringerPath);

그런 다음 해당 파일로 FileInputStream 작업을 수행하십시오.


1
콘텐츠 URI를 좋아하지 않는 어떤 호출을 사용하고 있습니까?
Phil Lello

1
모든 컨텐츠 URI를 때문에, 파일 경로를 얻을 수있는 콘텐츠의 URI 많다 filepaths은. 파일 경로를 사용하지 마십시오.
Mooing Duck

답변:


153

URI에서 getContentResolver().openInputStream(uri)가져 오기 위해 사용하십시오 InputStream.

http://developer.android.com/reference/android/content/ContentResolver.html#openInputStream(android.net.Uri)


12
선택자 활동에서 리턴 된 URI 구성표를 확인하십시오. uri.getScheme.equals ( "content") 인 경우 컨텐츠 분석기로여십시오. uri.Scheme.equals ( "file")이면 일반 파일 메소드를 사용하여여십시오. 어느 쪽이든, 공통 코드를 사용하여 처리 할 수있는 InputStream으로 끝납니다.
Jason LeBrun

16
실제로 getContentResolver (). openInputStream ()에 대한 문서를 다시 읽고 "content"또는 "file"체계에 대해 자동으로 작동하므로 체계를 확인할 필요 가 없습니다. 항상 content : // 또는 file : //이라고 가정하고 openInputStream ()은 항상 작동합니다.
Jason LeBrun

57
content : ...에서 InputStream 대신 File을 얻는 방법이 있습니까?
AlikElzin-kilaka

4
@kilaka 파일 경로를 얻을 수는 있지만 고통 스럽습니다. 참조 stackoverflow.com/a/20559418/294855
Danyal Aytekin

7
이 답변은 FileStream이 아닌 Files에 의존하지만 사용자가 파일을 선택할 수 있도록 OS를 사용하려는 비공개 소스 API를 사용하는 사람에게는 충분하지 않습니다. @DanyalAytekin이 언급 한 답변은 정확히 내가 필요한 것입니다 (실제로 어떤 종류의 파일을 사용하고 있는지 정확하게 알고 있기 때문에 지방을 많이 다룰 수있었습니다).
monkey0506

45

이것은 일부 특정 컨텐츠 해결 프로그램의 어려움을 극복하는 더 이상 사용되지 않는 해킹 된 방법으로 된 오래된 대답입니다. 엄청난 양의 소금과 함께 섭취하고 가능한 경우 적절한 openInputStream API를 사용하십시오.

Content Resolver를 사용 file://하여 content://URI 에서 경로 를 가져올 수 있습니다 .

String filePath = null;
Uri _uri = data.getData();
Log.d("","URI = "+ _uri);                                       
if (_uri != null && "content".equals(_uri.getScheme())) {
    Cursor cursor = this.getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
    cursor.moveToFirst();   
    filePath = cursor.getString(0);
    cursor.close();
} else {
    filePath = _uri.getPath();
}
Log.d("","Chosen path = "+ filePath);

1
고마워, 이것은 완벽하게 작동했습니다. 허용 된 답변이 제안하는 것처럼 InputStream을 사용할 수 없습니다.
ldam

4
이것은, 로컬 파일에 대해서만 작동 예를 들어 그것을하지 Google 드라이브에없는 일
bluewhile

1
때로는 작동하고 때로는 존재하지 않는 file : /// storage / emulated / 0 / ...을 반환합니다.
Reza Mohammadi

1
android.provider.MediaStore.Images.ImageColumns.DATA체계가 "_data"( ) 열이 항상 존재 content://합니까?
Edward Falk

2
이것은 주요 안티 패턴입니다. 일부 ContentProvider는 해당 열을 제공하지만 FileContentResolver를 우회하려고 할 때 읽기 / 쓰기 액세스 권한이있는 것은 아닙니다 . content://Google 엔지니어가 권장하는 공식 접근 방식 인 ContentResolver 메소드를 사용하여 URI에서 작동합니다 .
user1643723

9

콘텐츠 Uri가있는 content://com.externalstorage...경우이 방법을 사용 하여 Android 19 이상에서 폴더 또는 파일의 절대 경로를 가져올 수 있습니다 .

public static String getPath(final Context context, final Uri uri) {
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        System.out.println("getPath() uri: " + uri.toString());
        System.out.println("getPath() uri authority: " + uri.getAuthority());
        System.out.println("getPath() uri path: " + uri.getPath());

        // ExternalStorageProvider
        if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            System.out.println("getPath() docId: " + docId + ", split: " + split.length + ", type: " + type);

            // This is for checking Main Memory
            if ("primary".equalsIgnoreCase(type)) {
                if (split.length > 1) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1] + "/";
                } else {
                    return Environment.getExternalStorageDirectory() + "/";
                }
                // This is for checking SD Card
            } else {
                return "storage" + "/" + docId.replace(":", "/");
            }

        }
    }
    return null;
}

우리는 println을 사용하여 Uri의 각 부분을 확인할 수 있습니다. 내 SD 카드 및 장치 기본 메모리에 대한 반환 값은 다음과 같습니다. 파일이 메모리에 있지만 액세스 할 수는 있지만 이 방법을 사용하여 SD 카드에서 파일삭제할 수 없으며이 절대 경로를 사용하여 d 이미지를 읽거나 열 수 있습니다. 이 방법을 사용하여 삭제할 솔루션을 찾으면 공유하십시오. SD 카드

getPath() uri: content://com.android.externalstorage.documents/tree/612E-B7BF%3A/document/612E-B7BF%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/612E-B7BF:/document/612E-B7BF:
getPath() docId: 612E-B7BF:, split: 1, type: 612E-B7BF

메인 메모리

getPath() uri: content://com.android.externalstorage.documents/tree/primary%3A/document/primary%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/primary:/document/primary:
getPath() docId: primary:, split: 1, type: primary

file:///경로 사용 후 Uri를 받으려면

DocumentFile documentFile = DocumentFile.fromFile(new File(path));
documentFile.getUri() // will return a Uri with file Uri

나는 그것이 응용 프로그램에서 그것을하는 올바른 방법이라고 생각하지 않습니다. 슬프게도 나는 그것을
빠른

@Bondax 예, 파일 경로 또는 파일 Uris 대신 Content Uris로 작업해야합니다. 이것이 스토리지 액세스 프레임 워크가 도입 된 방식입니다. 그러나 파일 URI를 얻으려면 DocumentsContract 클래스를 사용하기 때문에 다른 답변의 가장 올바른 방법입니다. Github에서 Google 샘플을 확인하면이 클래스에서도 폴더의 하위 폴더를 가져 오는 데 사용됩니다.
Thracian

또한 DocumentFile 클래스는 API 19의 새로운 추가 기능과 SAF의 URI를 사용하는 방법을 제공합니다. 올바른 방법은 앱에 표준 경로를 사용하고 SAF ui를 통해 폴더에 대한 권한을 부여하고, Uri 문자열을 공유 환경 설정에 저장하고, DocumentFile 오브젝트가있는 폴더에 액세스해야하는 경우 문서에 액세스하는 것입니다.
Thracian

7

호출하여 content : // 체계로 URI를 처리하려고합니다. ContentResolver.query() 것은 좋은 해결책이 아닙니다. HTC Desire 4.2.2를 실행하면 쿼리 결과로 NULL을 얻을 수 있습니다.

대신 ContentResolver를 사용하지 않는 이유는 무엇입니까? https://stackoverflow.com/a/29141800/3205334


그러나 때때로 우리는 길만 필요합니다. 실제로 파일을 메모리에로드 할 필요는 없습니다.
Kimi Chiu

2
"경로"는 액세스 권한이 없으면 쓸모가 없습니다. 예를 들어, 애플리케이션 content://이 개인 내부 디렉토리의 파일에 해당하는 URI를 File제공하는 경우 새 Android 버전의 API 와 함께 해당 URI를 사용할 수 없습니다 . ContentResolver는 이러한 종류의 보안 제한을 극복하도록 설계되었습니다. ContentResolver에서 uri를 얻은 경우 작동 할 것으로 기대할 수 있습니다.
user1643723

5

영감을 얻은 답변은 Jason LaBrun & Darth Raven 입니다. 이미 답변 된 접근법을 시도하면 커서 null 사례 및 content : // 에서 file : // 로의 변환을 대부분 포함 할 수있는 솔루션이 나왔습니다.

파일을 변환하려면 uri에서 얻은 파일을 읽고 씁니다.

public static Uri getFilePathFromUri(Uri uri) throws IOException {
    String fileName = getFileName(uri);
    File file = new File(myContext.getExternalCacheDir(), fileName);
    file.createNewFile();
    try (OutputStream outputStream = new FileOutputStream(file);
         InputStream inputStream = myContext.getContentResolver().openInputStream(uri)) {
        FileUtil.copyStream(inputStream, outputStream); //Simply reads input to output stream
        outputStream.flush();
    }
    return Uri.fromFile(file);
}

파일 이름 사용을 얻으려면 커서 null 경우를 다룰 것입니다

public static String getFileName(Uri uri) {
    String fileName = getFileNameFromCursor(uri);
    if (fileName == null) {
        String fileExtension = getFileExtension(uri);
        fileName = "temp_file" + (fileExtension != null ? "." + fileExtension : "");
    } else if (!fileName.contains(".")) {
        String fileExtension = getFileExtension(uri);
        fileName = fileName + "." + fileExtension;
    }
    return fileName;
}

MIME 유형에서 파일 확장자로 변환하는 좋은 옵션이 있습니다

 public static String getFileExtension(Uri uri) {
    String fileType = myContext.getContentResolver().getType(uri);
    return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType);
}

파일명을 얻는 커서

public static String getFileNameFromCursor(Uri uri) {
    Cursor fileCursor = myContext.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
    String fileName = null;
    if (fileCursor != null && fileCursor.moveToFirst()) {
        int cIndex = fileCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        if (cIndex != -1) {
            fileName = fileCursor.getString(cIndex);
        }
    }
    return fileName;
}

고마워요, 일주일 동안 이것을보고있었습니다. 이를 위해 파일을 복사하는 것을 좋아하지 않지만 작동합니다.
justdan0227

3

글쎄, 답변이 늦었지만 코드가 테스트되었습니다.

URI에서 체계를 확인하십시오.

 byte[] videoBytes;

if (uri.getScheme().equals("content")){
        InputStream iStream =   context.getContentResolver().openInputStream(uri);
            videoBytes = getBytes(iStream);
        }else{
            File file = new File(uri.getPath());
            FileInputStream fileInputStream = new FileInputStream(file);     
            videoBytes = getBytes(fileInputStream);
        }

위의 대답에 나는 배열 바이트 비디오 URI를 변환,하지만이 질문에 관련이없는거야, 난 그냥의 사용을 보여주는 내 전체 코드를 복사 FileInputStream하고 InputStream모두로 내 코드에서 같은 노력하고 있습니다.

내 Fragment에서 getActivity () 변수 컨텍스트를 사용했으며 Activity에서는 단순히 ActivityName입니다.

context=getActivity(); // 조각

context=ActivityName.this;// 활동 중


질문과 관련이 없다는 것을 알고 있지만 어떻게 사용 byte[] videoBytes;하시겠습니까? 대부분의 답변 InputStream은 이미지와 함께 사용하는 방법 만 보여줍니다 .
KRK
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.