문제

Kitkat (또는 새 갤러리 이전) 이전 Intent.ACTION_GET_CONTENT 이런 URI를 반환했습니다

내용 : // 미디어/외부/이미지/미디어/3951.

사용 ContentResolver 그리고 쿼리MediaStore.Images.Media.DATA 파일 URL을 반환했습니다.

그러나 KitKat에서 갤러리는 다음과 같이 URI ( "Last"를 통해)를 반환합니다.

내용 : //com.android.providers.media.documents/document/image : 3951

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

도움이 되었습니까?

해결책 2

이 시도:

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

다른 팁

이것은 특별한 권한이 필요하지 않으며 스토리지 액세스 프레임 워크 및 비공식적 인 ContentProvider 패턴 (파일 경로 _data 필드).

/**
 * 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());
}

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

같은 문제가 있었고 위의 솔루션을 시도했지만 일반적으로 작동했지만 어떤 이유로 든 일부 이미지에 대한 URI 컨텐츠 제공 업체에 대한 허가 거부를 받고있었습니다. android.permission.MANAGE_DOCUMENTS 권한이 올바르게 추가되었습니다.

어쨌든 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 권한 깃발 등을 지속해야 할 수도 있고 일반적으로 보안 예외가 발생합니다 ...

다른 해결책은 ACTION_GET_CONTENT 결합 c.getContentResolver().openInputStream(selectedImageURI) Pre-KK와 KK에서 모두 작동합니다. KitKat은 새로운 문서보기를 사용하면 사진, 갤러리, 파일 탐색기, Dropbox, Google Drive 등과 같은 모든 앱과 함께 작동하지만이 솔루션을 사용할 때는 이미지를 작성해야합니다. onActivityResult() 예를 들어 SD 카드에 저장하십시오. 다음 앱 시작에서 저장된 URI 에서이 이미지를 재현하면 Google API 문서에 설명 된대로 권한 플래그를 추가 할 때에도 컨텐츠 리졸버에 보안 예외가 발생합니다 (일부 테스트를 수행했을 때 발생했습니다).

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

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

앱이 단순히 데이터를 읽고 가져 오도록하려면 Action_Get_Content를 사용하십시오. 이 접근법을 통해 앱은 이미지 파일과 같은 데이터 사본을 가져옵니다.

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

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. 그러나 그것은 또 다른 주제입니다.

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

4.4 (및 Google 드라이브) 이전에 URI는 다음과 같습니다.내용 : // 미디어/외부/이미지/미디어/41

질문에 언급 된 바와 같이, 그들은 더 자주 다음과 같이 보입니다.내용 : //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 ()는 I/O를 파일을 파일 아웃 PutStream에 복사하기 위해 파일을 작성합니다. 코드가 게시되지 않았습니다.

그냥 말하고 싶었습니다 이 답변 훌륭하고 문제없이 오랫동안 사용하고 있습니다. 하지만 얼마 전에 나는 다운로드 스프로더가 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;
}
}

파일 경로와 함께 발생하는 하나의 작업 솔루션으로 여러 답변을 결합합니다.

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

            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에서 실제 파일 경로를 얻는 방법

대답

내가 아는 한, 우리는 URI에서 파일 경로를 얻을 필요가 없습니다. 대부분의 경우 URI를 직접 사용하여 작업을 완료 할 수 있기 때문에 (1. 비트 맵을 얻는 등 2. 서버로 파일을 보내는 등 .))

1. 서버로 전송

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

URI를 사용하면 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에서 비트 맵을 얻습니다

URI가 이미지를 가리키면 BitMap을 얻을 수 있습니다.

/**
 * 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. Android는 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

이 Android 라이브러리는 KitKat의 사례 변경 사항을 처리합니다 (Oldere 버전 포함 -2.1+).
https://github.com/ipaulpro/afilechooser

사용 String path = FileUtils.getPath(context, uri) 반환 된 URI를 모든 OS 버전에서 사용할 수있는 경로 문자열로 변환합니다. 자세한 내용은 여기를 참조하십시오. https://stackoverflow.com/a/20559175/860488

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 메소드를 호출하기 전에 현재 SDK 버전이 23 이상인지 아닌지 확인하기 위해 IF State를 먼저 만들면 더 의미가 있어야합니다.

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

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() 메소드가 더 이상 사용되지 않으므로 사용하지 않습니다.

이 답변은 질문에 대한 M3N0R에서 나온 것입니다 Android는 uri.getpath ()의 진정한 길을 얻습니다. 그리고 나는 신용을 주장하지 않습니다. 나는이 문제를 아직 해결하지 못한 사람들이 이것을 사용할 수 있다고 생각했습니다.

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

레오의 영리한 솔루션을 기반으로합니다. 이 게시물에는이 작업을 수행하는 데 필요한 모든 코드가 포함되어야하며 모든 전화 및 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

가능하면 허가 및 LaunchImagePick을 확인하십시오

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
        }
    }
}

그것은 모든 사람들입니다. 이것은 내가 가진 모든 전화기에서 나에게 효과가 있습니다.

누구든지 관심이 있으시면 Kotlin 버전을 만들었습니다. ACTION_GET_CONTENT:

var path: String = uri.path // uri = any content Uri
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if ("/document/image:" in path || "/document/image%3A" in path) {
    // 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)
}

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);
    }
}

결과 이미지 데이터를 처리하기위한 결과 :

@override protected void onactivityResult (int requestCode, int resultCode, 의도 데이터) {

    //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();
        }


    }

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

그래서 설정을하면서 a DocumentsProvider, 나는 그것을 알아 차렸다 샘플 코드 (안에 getDocIdForFile, 주위 450)는 지정된 루트 (즉, 설정 한 내용)에 대한 파일 (고유 한) 경로를 기반으로 선택한 문서에 대한 고유 ID를 생성합니다. mBaseDir 96 행에).

그래서 URI는 다음과 같은 것을 보게됩니다.

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

문서가 말했듯이, 그것은 단일 뿌리 만 가정합니다 (내 경우에는 Environment.getExternalStorageDirectory() 그러나 당신은 다른 곳에서 사용할 수 있습니다 ... 그러면 루트에서 시작하여 파일 경로를 가져 와서 고유 한 ID, 선불로 만듭니다. "root:". 그래서 나는 "/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를 생성합니다.

(또한 "/"라고 가정하지 않는 경로를 구축하는 더 나은 방법이 있습니다. 그러나 경로 분리기 등이 있지만 아이디어를 얻습니다.)

이것은 나에게 잘 작동했습니다.

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());
    }
}

쌓아 올립니다 폴 버크의 대답 제안 된 "내장"기능의 대부분이 파일로 해결되지 않는 반환 경로의 대부분의 외부 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에서 잘 작동 함).

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

이 유형의 URI의 경우내용 : //com.android.providers.media.documents/Document/Document%3A19298또는 uri.getAuthority()이것들 중 하나입니다

"com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());

이 기능을 사용하십시오

private static String getDriveFilePath(Uri uri, Context context) {
        Uri returnUri = uri;
        Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);

        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
        returnCursor.moveToFirst();
        String name = (returnCursor.getString(nameIndex));
        String size = (Long.toString(returnCursor.getLong(sizeIndex)));
        File file = new File(context.getCacheDir(), name);
        try {
            InputStream inputStream = context.getContentResolver().openInputStream(uri);
            FileOutputStream outputStream = new FileOutputStream(file);
            int read = 0;
            int maxBufferSize = 1 * 1024 * 1024;
            int bytesAvailable = inputStream.available();

            //int bufferSize = 1024;
            int bufferSize = Math.min(bytesAvailable, maxBufferSize);

            final byte[] buffers = new byte[bufferSize];
            while ((read = inputStream.read(buffers)) != -1) {
                outputStream.write(buffers, 0, read);
            }
            Log.e("File Size", "Size " + file.length());
            inputStream.close();
            outputStream.close();
            Log.e("File Path", "Path " + file.getPath());
            Log.e("File Size", "Size " + file.length());
        } catch (Exception e) {
            Log.e("Exception", e.getMessage());
        }
        return file.getPath();
    }

@Paul Burke의 답변은 API 레벨 19 이상의 카메라 및 갤러리 사진 모두에서 잘 작동하지만 Android 프로젝트의 최소 SDK가 19 세 미만으로 설정되어 있지 않으며 위의 일부 답변이 갤러리와 갤러리와 갤러리와 갤러리와 모두 작동하지 않으면 작동하지 않습니다. 카메라. 글쎄, 나는 19 미만의 API 레벨에서 작동하는 @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;
}

귀하의 질문에 대한 답은 권한이 있어야한다는 것입니다. 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>`

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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top