Android 4.4 (KitKat)의 Android 갤러리는 의도에 대해 다른 URI를 반환합니다 .ACTION_GET_CONTENT


214

KitKat 이전 또는 새 갤러리 이전에 다음 Intent.ACTION_GET_CONTENT과 같은 URI를 반환했습니다.

content : // media / external / images / media / 3951.

ContentResolver및을 사용하여 MediaStore.Images.Media.DATA파일 URL 을 반환했습니다.

그러나 KitKat에서 갤러리는 다음과 같이 "마지막"을 통해 URI를 반환합니다.

content : //com.android.providers.media.documents/document/image : 3951

이것을 어떻게 처리합니까?


21
커프에서 파일에 직접 액세스 할 필요가없는 콘텐츠를 사용하는 방법을 찾았습니다. 예를 들어,를 Uri통해 스트림으로 열 수 있어야합니다 ContentResolver. 나는 content:// Uri파일을 나타내는가 항상로 변환 될 수 있다고 가정하는 앱에 대해 오랫동안 긴장 해 왔습니다 File.
— CommonsWare

1
@CommonsWare, sqlite db에 이미지 경로를 저장하여 나중에 열 수 있다면 URI 또는 ​​절대 파일 경로를 저장해야합니까?
— 뱀

2
@CommonsWare 나는 당신의 긴장에 동의합니다. :-) 그러나 파일 이름 (이미지의 경우)을 기본 코드로 전달할 수 있어야합니다. 해결책은 InputStreamon을 사용하여 얻은 데이터를 ContentResolver미리 지정된 위치 에 복사하여 알려진 파일 이름을 갖는 것입니다. 그러나 이것은 나에게 낭비되는 것처럼 들립니다. 다른 제안?
— darrenp

2
@ darrenp : 음 ..., InputStreamJNI를 통해 작동하도록 네이티브 코드를 다시 작성 하시겠습니까? 안타깝게도 많은 옵션이있는 것은 아닙니다.
— CommonsWare

1
알아두면 도움이됩니다. 답변 주셔서 감사합니다. 그 이후로 이미지를 파일이 아닌 메모리의 C ++로 이미지를 전달하여 파일 InputStream대신 사용할 수 있다는 것을 알게되었습니다 . EXIF 태그 읽기만 약간 까다 롭고 Drew Noakes의 라이브러리 가 필요합니다 . 귀하의 의견에 감사드립니다.
— darrenp

답변:


108

이 시도:

if (Build.VERSION.SDK_INT <19){
    Intent intent = new Intent(); 
    intent.setType("image/jpeg");
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED);
} else {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/jpeg");
    startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) return;
    if (null == data) return;
    Uri originalUri = null;
    if (requestCode == GALLERY_INTENT_CALLED) {
        originalUri = data.getData();
    } else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) {
        originalUri = data.getData();
        final int takeFlags = data.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // Check for the freshest data.
        getContentResolver().takePersistableUriPermission(originalUri, takeFlags);
    }

    loadSomeStreamAsynkTask(originalUri);

}

아마 필요

@SuppressLint ( "NewApi")

...에 대한

takePersistableUriPermission


1
KitKat 코드가 수행하는 작업을 자세히 설명 하시겠습니까? 새로운 허가가 필요합니까? KitKat에서도 사전 KitKat 코드가 작동합니다. KitKat에 다른 코드를 사용하기로 선택한 이유는 무엇입니까? 감사.
— Michael Greifeneder

67
새로운 SDK URI에서 길을 찾을 수없는 것 같습니다. 또한 적절한 문서와 발표없이 구글이 이런 종류의 변경을 한 것은 부끄러운 일입니다.
— user65721

1
파일 URL을 얻는 방법을 설명해 주시겠습니까? sdcard에서 실제 경로를 얻고 싶습니다. 예를 들어, 사진 인 경우 문서 Uri 대신 /storage/sdcard0/DCIM/Camera/IMG_20131118_153817_119.jpg 경로를 얻고 싶습니다.
— Álvaro

4
KitKat 문서 ( developer.android.com/about/versions/… )를 기반으로 다른 응용 프로그램에서 소유 한 문서를 사용 / 편집하지 않는 한 OP가 필요하지 않을 수 있습니다. OP가 사본을 원하거나 이전 버전과 일치하는 방식으로 작업을 수행하려면 @voytez의 답변이 더 적합합니다.
— Colin M.

8
이것은 나를 위해 작동하지 않습니다. 다음과 같은 예외가 발생합니다 (재고 4.4.2) : E / AndroidRuntime (29204) : 원인 : java.lang.SecurityException : 요청 된 플래그 0x1, 0x0 만 허용
— Russell Stewart

177

특별한 권한이 필요하지 않으며 비공식 ContentProvider패턴 ( _data필드의 파일 경로) 뿐만 아니라 Storage Access Framework 와도 작동합니다 .

/**
 * Get a file path from a Uri. This will get the the path for Storage Access
 * Framework Documents, as well as the _data field for the MediaStore and
 * other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
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)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @param selection (Optional) Filter used in the query.
 * @param selectionArgs (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri, String selection,
        String[] selectionArgs) {

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}


/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}

이 방법의 최신 버전은 여기를 참조하십시오 .


2
이 기능은 표준 갤러리 앱을 사용하는 4.4 Nexus 5 Documents UI 및 기타 이전 KitKat 기기에서 훌륭하게 작동했습니다.
— Josh

1
고마워요, sdk 19로 이것을 멀리하는 것은 나에게 오래 걸렸습니다! 내 문제는 내 장치가 Google 드라이브를 파일 브라우저로 사용하고 있다는 것입니다. 파일이 장치 이미지 경로에 있으면 파일 경로가 정상이지만 파일이 드라이브에 있으면 열리지 않습니다. 어쩌면 Google 드라이브에서 이미지를 여는 것을 처리해야 할 수도 있습니다. 내 응용 프로그램은 파일 경로를 사용하고 샘플링을 사용하여 이미지를 얻도록 작성되었습니다 ...
— RuAware

2
@RuAware 드라이브 파일을 선택하면 Authority: com.google.android.apps.docs.storage및이 나타납니다 Segments: [document, acc=1;doc=667]. 확실하지 않지만 doc값이 Uri쿼리 할 수 있는 ID 라고 가정합니다 . "Android에서 앱 인증"( developers.google.com/drive/integrate-android-ui) 에 설명 된대로 권한을 설정해야합니다 . 알아 내려면 여기를 업데이트하십시오.
— Paul Burke

30
이것은 절대적으로 끔찍하다! 이와 같이 "속임수"코드를 계속 전파해서는 안됩니다. 패턴을 알고있는 소스 앱만 지원하며 문서 제공자 모델의 요점은 임의의 소스를 지원하는 것입니다.
— j__m

2
는 _data컨텐트 프로를 지원하지 않을 경우 작동하지 않을 것입니다. @CommonsWare 지침 을 따르는 것이 더 이상 전체 파일 경로를 사용하지 않는 것이 좋습니다 . 실제 파일이 아닌 Dropbox 클라우드의 파일 일 수 있기 때문입니다.
— soshial

67

같은 문제가 있었지만 위의 해결책을 시도했지만 일반적으로 효과가 있었지만 어떤 이유로 든 android.permission.MANAGE_DOCUMENTS권한이 올바르게 추가 되었지만 일부 이미지에 대한 Uri 컨텐츠 공급자에 대한 권한 거부가 발생했습니다 .

어쨌든 KITKAT 문서보기 대신 이미지 갤러리를 강제로 여는 다른 솔루션을 찾았습니다.

// KITKAT

i = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, CHOOSE_IMAGE_REQUEST);

그런 다음 이미지를로드하십시오.

Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
                BitmapFactory.decodeStream(input , null, opts);

편집하다

ACTION_OPEN_DOCUMENT 권한 플래그 등을 유지해야하며 일반적으로 보안 예외가 발생합니다 ...

다른 해결책은 preKK와 KK 모두에서 작동 하는 ACTION_GET_CONTENT조합 을 사용 c.getContentResolver().openInputStream(selectedImageURI)하는 것입니다. Kitkat은 새로운 문서보기를 사용 하고이 솔루션은 사진, 갤러리, 파일 탐색기, Dropbox, Google 드라이브 등과 같은 모든 앱에서 작동하지만이 솔루션을 사용할 때 이미지를 만들어 onActivityResult()저장해야 한다는 것을 기억하십시오 예를 들어 SD 카드. 다음 앱 실행시 저장된 URI에서이 이미지를 다시 생성하면 Google API 문서에 설명 된대로 권한 플래그를 추가하더라도 콘텐츠 확인자에 보안 예외가 발생합니다.

또한 Android 개발자 API 가이드 라인은 다음을 제안합니다.

ACTION_OPEN_DOCUMENT는 ACTION_GET_CONTENT를 대체하기위한 것이 아닙니다. 사용해야하는 것은 앱의 요구에 따라 다릅니다.

앱이 단순히 데이터를 읽거나 가져 오도록하려면 ACTION_GET_CONTENT를 사용하십시오. 이 방법을 사용하면 앱은 이미지 파일과 같은 데이터 사본을 가져옵니다.

앱이 문서 공급자가 소유 한 문서에 장기간 지속적으로 액세스하도록하려면 ACTION_OPEN_DOCUMENT를 사용하십시오. 예를 들어 사용자가 문서 공급자에 저장된 이미지를 편집 할 수있는 사진 편집 앱이 있습니다.


1
이 답변에는 제 목적에 맞는 올바른 정보가 포함되어 있습니다. KitKat에서 ACTION_PICK 및 EXTERNAL_CONTENT_URI를 조건부로 사용하면 ACTION_GET_CONTENT를 사용하는 이전 버전과 마찬가지로 ContentResolver를 통해 갤러리의 이미지에 대한 메타 데이터를 얻는 동일한 기능을 제공했습니다.
— Colin M.

@voytez,이 URI가 메시지를 통해 반환되어 이미지의 실제 경로로 변환 될 수 있습니까?
— 뱀

이 코드는 KK 문서보기 대신 이미지 갤러리를 강제로 열기 때문에 KitKat 이전처럼 작동해야합니다. 그러나 이미지를 만들기 위해 이미지를 사용하려는 경우 실제 경로로 변환하는 것이 불필요한 단계이므로이 솔루션이 더 좋습니다.
— voytez

4
대신 나를 위해 일했습니다 Intent.ACTION_GET_CONTENT. 어쨌든 나는 Intent.createChooser()래퍼를 new에 유지하여 Intent사용자가 탐색 할 응용 프로그램을 선택할 수있게하고 예상대로 작동했습니다. 누군가이 솔루션의 단점을 볼 수 있습니까?
— lorenzo-s

1
인용문을 궁금해하는 사람은 developer.android.com/guide/topics/providers/
— snark

38

Commonsware가 언급했듯이 스트림을 통해 스트림 ContentResolver을 파일로 변환 할 수 있다고 가정해서는 안됩니다 .

당신이 정말해야 할 것은 열 수 InputStream로부터를 ContentProvider다음에서 비트 맵을 만듭니다. 또한 4.4 이하 버전에서도 작동하므로 반영 할 필요가 없습니다.

    //cxt -> current context

    InputStream input;
    Bitmap bmp;
    try {
        input = cxt.getContentResolver().openInputStream(fileUri);
        bmp = BitmapFactory.decodeStream(input);
    } catch (FileNotFoundException e1) {

    }

당신이 큰 이미지를 처리 할 경우 물론, 당신은 적절한 그들을로드해야합니다 inSampleSize: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . 그러나 그것은 또 다른 주제입니다.


이것은 Kitkat을 실행하는 Nexus 4에서는 작동하지 않지만 4.1.2를 실행하는 Galaxy S3에서는 작동합니다.
— Dan2552

@ Dan2552 어느 부분이 작동하지 않습니까? 예외가 있습니까?
— Michał K

갤러리에 잘못된 의도 호출을 사용하고 있음이 밝혀졌습니다. 나는 모든 종류의 문서를 위해 하나를 사용했지만 파일 확장자 필터를 사용했습니다.
— Dan2552

2
정말 아름다운 대답입니다, 감사합니다! 이 답변을 따르는 다른 사람들의 경우 'cxt'는 현재 컨텍스트를 나타내며 일반적으로 'this'입니다.
— thomasforth

1
이것은 아마도 파일이 없다는 것을 의미합니다. URI가 정상인 것 같습니다.
— Michał K

33

이미 게시 된 답변은 사람들이 올바른 방향으로 나아가도록해야한다고 생각합니다. 그러나 여기 내가 업데이트 한 레거시 코드에 대한 의미가 있습니다. 레거시 코드는 갤러리의 URI를 사용하여 이미지를 변경 한 다음 저장했습니다.

4.4 (및 Google 드라이브) 이전의 URI는 다음과 같습니다. content : // media / external / images / media / 41

질문에 명시된 바와 같이 그들은 종종 다음과 같이 보입니다 : content : //com.android.providers.media.documents/document/image : 3951

이미지를 저장하고 기존 코드를 방해하지 않는 기능이 필요했기 때문에 갤러리의 URI를 앱의 데이터 폴더로 복사했습니다. 그런 다음 데이터 폴더의 저장된 이미지 파일에서 새 URI를 시작했습니다.

아이디어는 다음과 같습니다.

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent), CHOOSE_IMAGE_REQUEST);

public void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image");

    //Copy URI contents into temporary file.
    try {
        tempFile.createNewFile();
        copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile));
    }
    catch (IOException e) {
        //Log Error
    }

    //Now fetch the new URI
    Uri newUri = Uri.fromFile(tempFile);

    /* Use new URI object just like you used to */
 }

참고-copyAndClose ()는 InputStream을 FileOutputStream에 복사하기 위해 파일 I / O를 수행합니다. 코드가 게시되지 않았습니다.


꽤 영리합니다.
— 나도

당신은 나의 영웅입니다. 이것이 바로 제가 필요한 것입니다! Google 드라이브 파일에도 효과적입니다.
— mishkin

상자 밖에서 생각하세요? : D이 코드는 내가 예상했던대로 작동합니다.
— WeirdElfB0y

2
copyAndClose에 대한 코드를 게시, 답변이 완료되지 않았습니다
— Bartek Pacia

24

이 답변 이 훌륭 하다고 말하고 싶었고 오랫동안 문제없이 사용하고 있습니다. 그러나 얼마 전 DownloadsProvider가 URI를 형식으로 반환하는 문제 content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf가 발생 NumberFormatException하여 URI 세그먼트를 오랫동안 구문 분석 할 수 없으므로 앱이 중단됩니다 . 그러나 raw:세그먼트에는 참조 파일을 검색하는 데 사용할 수있는 direct uri가 포함되어 있습니다. 그래서 isDownloadsDocument(uri) if내용을 다음과 같이 바꿔서 고쳤습니다.

final String id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id)) {
if (id.startsWith("raw:")) {
    return id.replaceFirst("raw:", "");
}
try {
    final Uri contentUri = ContentUris.withAppendedId(
            Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
    return getDataColumn(context, contentUri, null, null);
} catch (NumberFormatException e) {
    Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toString(), e);
    return null;
}
}

2
완벽하게 작동합니다! 감사합니다
— mikemike396

8

여러 답변을 하나의 작동 솔루션으로 결합하여 파일 경로를 생성합니다.

Mime 유형은 예제 목적과 관련이 없습니다.

            Intent intent;
            if(Build.VERSION.SDK_INT >= 19){
                intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
                intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
            }else{
                intent = new Intent(Intent.ACTION_GET_CONTENT);
            }
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setType("application/octet-stream");
            if(isAdded()){
                startActivityForResult(intent, RESULT_CODE);
            }

취급 결과

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) {
        Uri uri = data.getData();
        if (uri != null && !uri.toString().isEmpty()) {
            if(Build.VERSION.SDK_INT >= 19){
                final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
                //noinspection ResourceType
                getActivity().getContentResolver()
                        .takePersistableUriPermission(uri, takeFlags);
            }
            String filePath = FilePickUtils.getSmartFilePath(getActivity(), uri);
            // do what you need with it...
        }
    }
}

FilePickUtils

import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class FilePickUtils {
    private static String getPathDeprecated(Context ctx, Uri uri) {
        if( uri == null ) {
            return null;
        }
        String[] projection = { MediaStore.Images.Media.DATA };
        Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null);
        if( cursor != null ){
            int column_index = cursor
                    .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        }
        return uri.getPath();
    }

    public static String getSmartFilePath(Context ctx, Uri uri){

        if (Build.VERSION.SDK_INT < 19) {
            return getPathDeprecated(ctx, uri);
        }
        return  FilePickUtils.getPath(ctx, uri);
    }

    @SuppressLint("NewApi")
    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)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(
                        Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                return getDataColumn(context, contentUri, null, null);
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[] {
                        split[1]
                };

                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    /**
     * Get the value of the data column for this Uri. This is useful for
     * MediaStore Uris, and other file-based ContentProviders.
     *
     * @param context The context.
     * @param uri The Uri to query.
     * @param selection (Optional) Filter used in the query.
     * @param selectionArgs (Optional) Selection arguments used in the query.
     * @return The value of the _data column, which is typically a file path.
     */
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

}

문제가 발생했습니다 .... uri.getPath ()가 / external을 사용하여 uri를 반환하고 경로를 반환하지 않았습니다. 만약 내가 ( "content".equalsIgnoreCase (uri.getScheme ())) 블록을 추가하고 이것이 지금 잘 작동 .. 그것이 무엇을 설명 할 수
— 있습니까

filePath가 null로 표시됩니다. uri : content : //com.android.externalstorage.documents/document/799B-1419%3AScreenshot%2FScreenshot_20181117_162826.png
— Bhavesh Moradiya 2018

7

질문

URI에서 실제 파일 경로를 얻는 방법

대답

내 지식으로는, 대부분의 경우 URI를 직접 사용하여 작업을 수행 할 수 있기 때문에 URI에서 파일 경로를 가져올 필요가 없습니다 (예 : 비트 맵 가져 오기 2. 서버로 파일 보내기 등) .)

1. 서버로 보내기

URI 만 사용하여 파일을 서버로 직접 보낼 수 있습니다.

URI를 사용하여 MultiStreamEntity를 사용하여 서버로 직접 보낼 수있는 InputStream을 얻을 수 있습니다.

예

/**
 * Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application).
 *
 * @param uri     URI.
 * @param context Context.
 * @return Multi Part Entity.
 */
public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) {
    MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8"));
    try {
        InputStream inputStream = mContext.getContentResolver().openInputStream(uri);
        if (inputStream != null) {
            ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context));
            multipartEntity.addPart("[YOUR_KEY]", contentBody);
        }
    }
    catch (Exception exp) {
        Log.e("TAG", exp.getMessage());
    }
    return multipartEntity;
}

/**
 * Used to get a file name from a URI.
 *
 * @param uri     URI.
 * @param context Context.
 * @return File name from URI.
 */
public String getFileNameFromUri(final Uri uri, final Context context) {

    String fileName = null;
    if (uri != null) {
        // Get file name.
        // File Scheme.
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
            File file = new File(uri.getPath());
            fileName = file.getName();
        }
        // Content Scheme.
        else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
            Cursor returnCursor =
                    context.getContentResolver().query(uri, null, null, null, null);
            if (returnCursor != null && returnCursor.moveToFirst()) {
                int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                fileName = returnCursor.getString(nameIndex);
                returnCursor.close();
            }
        }
    }
    return fileName;
}

2. URI에서 BitMap 얻기

URI가 이미지를 가리키는 경우 비트 맵을 얻습니다. 그렇지 않으면 null입니다.

/**
 * Used to create bitmap for the given URI.
 * <p>
 * 1. Convert the given URI to bitmap.
 * 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap.
 * 3. Create bitmap bitmap depending on the ration from URI.
 * 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri
 *
 * @param context       Context.
 * @param uri           URI to the file.
 * @param bitmapSize Bitmap size required in PX.
 * @return Bitmap bitmap created for the given URI.
 * @throws IOException
 */
public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException {

    // 1. Convert the given URI to bitmap.
    InputStream input = context.getContentResolver().openInputStream(uri);
    BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
    onlyBoundsOptions.inJustDecodeBounds = true;
    onlyBoundsOptions.inDither = true;//optional
    onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
    input.close();
    if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
        return null;
    }

    // 2. Calculate ratio.
    int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth;
    double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0;

    // 3. Create bitmap.
    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
    bitmapOptions.inDither = true;//optional
    bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional
    input = context.getContentResolver().openInputStream(uri);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
    input.close();

    return bitmap;
}

/**
 * For Bitmap option inSampleSize - We need to give value in power of two.
 *
 * @param ratio Ratio to be rounded of to power of two.
 * @return Ratio rounded of to nearest power of two.
 */
private static int getPowerOfTwoForSampleRatio(final double ratio) {
    int k = Integer.highestOneBit((int) Math.floor(ratio));
    if (k == 0) return 1;
    else return k;
}

코멘트

  1. 안드로이드는 URI에서 파일 경로를 얻는 방법을 제공하지 않으며 위의 답변 대부분에서 기능 릴리스에서 깨질 수있는 상수를 하드 코딩했습니다 (죄송합니다, 잘못되었을 수 있습니다).
  2. URI에서 파일 경로 가져 오기 솔루션으로 직접 이동하기 전에 URI 및 Android 기본 메소드로 유스 케이스를 해결할 수 있는지 확인하십시오.

참고

  1. https://developer.android.com/guide/topics/providers/content-provider-basics.html
  2. https://developer.android.com/reference/android/content/ContentResolver.html
  3. https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html

감사합니다. 이 방법으로 Uri와 ContentResolver를 사용하면 파일을 다룰 때 응용 프로그램이 크게 단순화되었습니다.
— jacksonakj


3

Android SDK 버전 23 이상에서 @Paul Burke의 코드를 계속 사용하는 사용자의 경우 프로젝트에서 EXTERNAL_PERMISSION이 누락되었다는 오류가 발생하고 AndroidManifest.xml 파일에 이미 사용자 권한을 추가 한 것이 확실합니다. Android API 23 이상에서 Google이 런타임에 파일에 액세스하기위한 조치를 수행하는 동안 다시 권한을 보장해야하기 때문입니다.

즉, SDK 버전이 23 이상인 경우 그림 파일을 선택하고 해당 URI를 알고 자하는 동안 읽기 및 쓰기 권한이 필요합니다.

다음은 Paul Burke의 솔루션 외에도 내 코드입니다. 이 코드를 추가하면 프로젝트가 제대로 작동하기 시작합니다.

private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSINOS_STORAGE = {
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.WRITE_EXTERNAL_STORAGE
};

public static void verifyStoragePermissions(Activity activity) {
    int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(
                activity,
                PERMISSINOS_STORAGE,
                REQUEST_EXTERNAL_STORAGE
        );
    }
}

그리고 URI를 요청하는 활동 및 조각에서 :

private void pickPhotoFromGallery() {

    CompatUtils.verifyStoragePermissions(this);
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    // startActivityForResult(intent, REQUEST_PHOTO_LIBRARY);
    startActivityForResult(Intent.createChooser(intent, "选择照片"),
            REQUEST_PHOTO_LIBRARY);
}

필자의 경우 CompatUtils.java는 verifyStoragePermissions 메소드를 정의하는 곳입니다 (정적 유형으로 다른 활동 내에서 호출 할 수 있음).

또한 verifyStoragePermissions 메소드를 호출하기 전에 if 상태를 먼저 작성하여 현재 SDK 버전이 23 이상인지 여부를 확인하는 것이 더 합리적입니다.


2

이것이 제가하는 것입니다:

Uri selectedImageURI = data.getData();    imageFile = new File(getRealPathFromURI(selectedImageURI)); 

private String getRealPathFromURI(Uri contentURI) {
  Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
  if (cursor == null) { // Source is Dropbox or other similar local file path
      return contentURI.getPath();
      } else { 
      cursor.moveToFirst(); 
      int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); 
      return cursor.getString(idx); 
  }
}

참고 : managedQuery()방법은 더 이상 사용되지 않으므로 사용하지 않습니다.

이 답변은 질문 android의 m3n0R에서 Uri.getPath ()로 실제 경로를 얻었 으며 크레딧이 없다고 주장합니다. 나는이 문제를 아직 해결하지 않은 사람들이 이것을 사용할 수 있다고 생각했습니다.


2
KitKat의 새로운 갤러리 앱 (엄밀히 "미디어 문서 제공자"앱)에 대한 답변은 아닙니다.
— nagoya0

질문자가 "갤러리"라고 부르는 앱은 kitkat의 새로운 파일 선택 기일 것입니다. 참고 : addictivetips.com/android/…
— nagoya0

이 행에서 Nexus 5X, Android 6에서 IndexOutOfBound를 비슷하게 얻었습니다.cursor.getString(idx);
— 8:26에

1

takePersistableUriPermission 메소드를 사용하면 런타임 예외가 발생하지 않으므로 사용하지 마십시오. / ** * 갤러리에서 선택하십시오. * /

public void selectFromGallery() {
    if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) {

        Intent intent = new Intent(); 
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        ((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED);

    } else {

        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        ((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED);
    }
}

이미지 데이터를 처리하는 결과에 대한 OnActivity :

@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) {

    //gallery intent result handling before kit-kat version
    if(requestCode==AppConstants.GALLERY_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        String[] filePathColumn = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String filePath = cursor.getString(columnIndex);
        cursor.close();
        photoFile = new File(filePath);
        mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP);

    }
    //gallery intent result handling after kit-kat version
    else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED 
            && resultCode == RESULT_OK) {

        Uri selectedImage = data.getData();
        InputStream input = null;
        OutputStream output = null;

        try {
            //converting the input stream into file to crop the 
            //selected image from sd-card.
            input = getApplicationContext().getContentResolver().openInputStream(selectedImage);
            try {
                photoFile = mImgCropping.createImageFile();
            } catch (IOException e) {
                e.printStackTrace();
            }catch(Exception e) {
                e.printStackTrace();
            }
            output = new FileOutputStream(photoFile);

            int read = 0;
            byte[] bytes = new byte[1024];

            while ((read = input.read(bytes)) != -1) {
                try {
                    output.write(bytes, 0, read);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

두 번째 경우에 이미지를 어디에 표시합니까?
— Quantum_VC

죄송합니다. mImgCropping.startCropImage (photoFile, AppConstants.REQUEST_IMAGE_CROP);이 줄을 다른 코드에 추가하지 못했습니다. 내 프로젝트 요구에 따라 다시 이미지 자르기 기능을 호출해야합니다
— saranya

photoFile 및 mImgCropping은 어떤 파일 형식입니까?
— Philip BH

1

관심있는 사람은 다음을 위해 Kotlin 버전을 만들었습니다 ACTION_GET_CONTENT.

var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (path.contains("/document/image:")) { // files selected from "Documents"
    databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    selection = "_id=?"
    selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
} else { // files selected from all other sources, especially on Samsung devices
    databaseUri = uri
    selection = null
    selectionArgs = null
}
try {
    val projection = arrayOf(MediaStore.Images.Media.DATA,
        MediaStore.Images.Media._ID,
        MediaStore.Images.Media.ORIENTATION,
        MediaStore.Images.Media.DATE_TAKEN) // some example data you can query
    val cursor = context.contentResolver.query(databaseUri,
        projection, selection, selectionArgs, null)
    if (cursor.moveToFirst()) {
        // do whatever you like with the data
    }
    cursor.close()
} catch (e: Exception) {
    Log.e(TAG, e.message, e)
}

나는 단지 kotlin의 작업 코드를 원합니다. 그것은 나를 위해 일하고 있습니다. 감사합니다
— Harvi Sirja

1

여기에 몇 가지 답변을 시도했지만 매번 작동하고 권한을 관리하는 솔루션이 있다고 생각합니다.

LEO의 영리한 솔루션을 기반으로합니다. 이 게시물에는이 작업을 수행하는 데 필요한 모든 코드가 포함되어 있어야하며 모든 휴대 전화 및 Android 버전에서 작동해야합니다.)

SD 카드에서 파일을 선택할 수 있으려면 매니페스트에 파일이 필요합니다.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

상수 :

private static final int PICK_IMAGE = 456; // Whatever number you like
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like
public static final String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like

권한을 확인하고 가능하면 ImagePick을 시작하십시오.

if (ContextCompat.checkSelfPermission(getThis(),
        Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {

    ActivityCompat.requestPermissions(getThis(),
            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL);
}
else {
    launchImagePick();
}

권한 응답

@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull
                                         String permissions[],
                                       @NonNull
                                         int[] grantResults) {

    if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) {
        launchImagePick();
    }
}

권한 응답 관리

public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) {

    if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) {

        // If request is cancelled, the result arrays are empty.

        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            // Permission was granted, yay! Do the
            // contacts-related task you need to do.
            return true;

        } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {

            boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity,
                    Manifest.permission.READ_EXTERNAL_STORAGE);

            if (!showRationale) {
                // The user also CHECKED "never ask again".
                // You can either enable some fall back,
                // disable features of your app
                // or open another dialog explaining
                // again the permission and directing to
                // the app setting.

            } else {
                // The user did NOT check "never ask again".
                // This is a good place to explain the user
                // why you need the permission and ask if he/she wants
                // to accept it (the rationale).
            }
        } else {
            // Permission denied, boo! Disable the
            // functionality that depends on this permission.
        }
    }
    return false;
}

이미지 선택 시작

private void launchImagePick() {

    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    startActivityForResult(intent, PICK_IMAGE);

    // see onActivityResult
}

이미지 선택 응답 관리

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE) {

        if (resultCode == Activity.RESULT_OK) {
            if (data != null && data.getData() != null) {

                try {
                     InputStream inputStream = getContentResolver().openInputStream(data.getData())
                     if (inputStream != null) {

                        // No special persmission needed to store the file like that
                        FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE);

                        final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead = -1;
                        while ((bytesRead = inputStream.read(buffer)) > -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                        inputStream.close();
                        fos.close();

                        File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME);

                        // Do whatever you want with the File

                        // Delete when not needed anymore
                        deleteFile(FILE_TEMP_NAME);
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                // Error display
            }
        } else {
            // The user did not select any image
        }
    }
}

그게 다야. 이것은 내가 가진 모든 전화에서 나를 위해 작동합니다.


0

이것은 완전한 해킹이지만 여기에 내가 한 일이 있습니다 ...

따라서 DocumentsProvider 설정과 함께 재생하는 동안 샘플 코드 ( getDocIdForFile450 행 부근)는 지정된 루트에 상대적인 파일 (고유) 경로를 기반으로 선택한 문서의 고유 ID를 생성 한다는 것을 알았 습니다 (즉, mBaseDir96 번 라인에서 설정 한 내용 ).

따라서 URI는 다음과 같이 보입니다.

content://com.example.provider/document/root:path/to/the/file

문서에서 알 수 있듯이 단일 루트 만 가정합니다 (제 경우에는 Environment.getExternalStorageDirectory()다른 곳을 사용할 수 있습니다 ... 그런 다음 루트에서 시작하여 파일 경로를 가져 와서 " root:"를 추가하여 고유 ID로 만듭니다 . "/document/root:uri.getPath ()에서 "부분을 제거하여 다음과 같은 방법으로 실제 파일 경로를 만들어 경로를 확인할 수 있습니다.

public void onActivityResult(int requestCode, int resultCode, Intent data) {
// check resultcodes and such, then...
uri = data.getData();
if (uri.getAuthority().equals("com.example.provider"))  {
    String path = Environment.getExternalStorageDirectory(0.toString()
                 .concat("/")
                 .concat(uri.getPath().substring("/document/root:".length())));
    doSomethingWithThePath(path); }
else {
    // another provider (maybe a cloud-based service such as GDrive)
    // created this uri.  So handle it, or don't.  You can allow specific
    // local filesystem providers, filter non-filesystem path results, etc.
}

알아. 부끄러운 일이지만 효과가있었습니다. 다시 말하지만, 이것은 당신이 당신의 사용에 의존 자신의 문서 ID를 생성하기 위해 앱에서 문서를 제공합니다.

(또한 "/"를 경로 구분자로 가정하지 않는 경로를 만드는 더 좋은 방법이 있습니다. 그러나 아이디어를 얻습니다.)


앱이 이미 file://외부 파일 선택기의 의도를 처리 하는 경우 위의 예와 같이 권한을 확인하여 사용자 지정 공급자의 권한인지 확인할 수 있습니다. 또한 file://추출한 경로를 사용하여 새로운 의도 를 "위조"하는 경로를 사용 StartActivity()하여 앱이 선택하도록하십시오. 나도 알아
— fattire

0

이것은 나를 위해 잘 작동했습니다 :

else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK)
{
    Uri uri = data.getData();
    Log.i(TAG, "old uri =  " + uri);
    dumpImageMetaData(uri);

    try {
        ParcelFileDescriptor parcelFileDescriptor =
                getContentResolver().openFileDescriptor(uri, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Log.i(TAG, "File descriptor " + fileDescriptor.toString());

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);

        options.inSampleSize =
           BitmapHelper.calculateInSampleSize(options,
                                              User.PICTURE_MAX_WIDTH_IN_PIXELS,
                                              User.PICTURE_MAX_HEIGHT_IN_PIXELS);
        options.inJustDecodeBounds = false;

        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
        imageViewPic.setImageBitmap(bitmap);

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        // get byte array here
        byte[] picData = stream.toByteArray();
        ParseFile picFile = new ParseFile(picData);
        user.setProfilePicture(picFile);
    }
    catch(FileNotFoundException exc)
    {
        Log.i(TAG, "File not found: " + exc.toString());
    }
}

dumpImageMetaData (uri)를 잊어 버리십시오. 불필요
— Rafa

0

Paul Burke의 답변을 바탕으로 외부 SD 카드의 URI 경로를 해결하는 데 많은 문제가 발생했습니다. 제안 된 "내장"기능의 대부분이 파일로 해석되지 않는 경로를 반환하기 때문입니다.

그러나 이것은 // TODO 기본이 아닌 볼륨을 처리하는 나의 접근 방식입니다 .

String resolvedPath = "";
File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
for (File f : possibleExtSdComposites) {
    // Reset final path
    resolvedPath = "";

    // Construct list of folders
    ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));

    // Look for folder "<your_application_id>"
    int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID);

    // ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
    ArrayList<String> hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2));

    // Construct list containing full possible path to the file
    hierarchyList.add(tail);
    String possibleFilePath = TextUtils.join("/", hierarchyList);

    // If file is found --> success
    if (idx != -1 && new File(possibleFilePath).exists()) {
        resolvedPath = possibleFilePath;
        break;
    }
}

if (!resolvedPath.equals("")) {
    return resolvedPath;
} else {
    return null;
}

그것은 모든 휴대 전화 제조업체마다 다를 수있는 계층 구조에 달려 있습니다. 모두 테스트하지는 않았습니다 (Xperia Z3 API 23 및 Samsung Galaxy A3 API 23에서 지금까지 잘 작동했습니다).

다른 곳에서는 잘 작동하지 않는지 확인하십시오.


-1

@paul burke의 답변은 API 레벨 19 이상의 카메라 및 갤러리 사진 모두에서 잘 작동하지만 Android 프로젝트의 최소 SDK가 19 미만으로 설정되어 있으면 작동하지 않으며 위의 일부 답변은 갤러리 및 카메라. 글쎄, API 레벨 19 이하에서 작동하는 @paul burke의 코드를 수정했습니다. 아래는 코드입니다.

public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >=
                             Build.VERSION_CODES.KITKAT;
    Log.i("URI",uri+"");
    String result = uri+"";

    // DocumentProvider
    // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
    if (isKitKat && (result.contains("media.documents"))) {

        String[] ary = result.split("/");
        int length = ary.length;
        String imgary = ary[length-1];
        final String[] dat = imgary.split("%3A");

        final String docId = dat[1];
        final String type = dat[0];

        Uri contentUri = null;
        if ("image".equals(type)) {
            contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        }
        else if ("video".equals(type)) {
        }
        else if ("audio".equals(type)) {
        }

        final String selection = "_id=?";
        final String[] selectionArgs = new String[] {
            dat[1]
        };

        return getDataColumn(context, contentUri, selection, selectionArgs);
    }
    else
    if ("content".equalsIgnoreCase(uri.getScheme())) {
        return getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

public static String getDataColumn(Context context, Uri uri, String selection,
                                   String[] selectionArgs) {
    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    }
    finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}

java.lang.IllegalArgumentException이 발생했습니다. Google 문서 이미지를 선택할 때 요청한 열을 제공 할 수 없습니다.
— dirkoneill

@dirkoneill 같은 예외가 발생합니다. 픽스를 찾았습니까?
— Henry

-4

귀하의 질문에 대한 답변은 권한이 필요하다는 것입니다. manifest.xml 파일에 다음 코드를 입력하십시오.

<uses-sdk  android:minSdkVersion="8"   android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission>
<uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`

그것은 나를 위해 일했다 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.