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.