Get real path from URI, Android KitKat new storage

2018-12-31 03:15发布

Before the new gallery access in Android 4.4 (KitKat) I got my real path on the SD card with this method:

public String getPath(Uri uri) {
   String[] projection = { MediaStore.Images.Media.DATA };
   Cursor cursor = managedQuery(uri, projection, null, null, null);
   startManagingCursor(cursor);
   int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
   cursor.moveToFirst();
 return cursor.getString(column_index);
}

Now, the Intent.ACTION_GET_CONTENT return different data:

Before:

content://media/external/images/media/62

Now:

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

How could I manage to obtain the real path on the SD card?

9条回答
弹指情弦暗扣
2楼-- · 2018-12-31 03:49

Try this:

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

Use the following in the onActivityResult:

Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
BitmapFactory.decodeStream(input , null, opts);
查看更多
只若初见
3楼-- · 2018-12-31 03:51

This will get the file path from the MediaProvider, DownloadsProvider, and ExternalStorageProvider, while falling back to the unofficial ContentProvider method you mention.

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

These are taken from my open source library, aFileChooser.

查看更多
君临天下
4楼-- · 2018-12-31 03:51

This answer is based on your somewhat vague description. I assume that you fired an intent with action: Intent.ACTION_GET_CONTENT

And now you get content://com.android.providers.media.documents/document/image:62 back instead of the previously media provider URI, correct?

On Android 4.4 (KitKat) the new DocumentsActivity gets opened when an Intent.ACTION_GET_CONTENT is fired thus leading to grid view (or list view) where you can pick an image, this will return the following URIs to calling context (example): content://com.android.providers.media.documents/document/image:62 (these are the URIs to the new document provider, it abstracts away the underlying data by providing generic document provider URIs to clients).

You can however access both gallery and other activities responding to Intent.ACTION_GET_CONTENT by using the drawer in the DocumentsActivity (drag from left to right and you'll see a drawer UI with Gallery to choose from). Just as pre KitKat.

If you still which to pick in DocumentsActivity class and need the file URI, you should be able to do the following (warning this is hacky!) query (with contentresolver):content://com.android.providers.media.documents/document/image:62 URI and read the _display_name value from the cursor. This is somewhat unique name (just the filename on local files) and use that in a selection (when querying) to mediaprovider to get the correct row corresponding to this selection from here you can fetch the file URI as well.

The recommended ways of accessing document provider can be found here (get an inputstream or file descriptor to read file/bitmap):

Examples of using documentprovider

查看更多
牵手、夕阳
5楼-- · 2018-12-31 03:54

We need to do the following changes/fixes in our earlier onActivityResult()'s gallery picker code to run seamlessly on Android 4.4 (KitKat) and on all other earlier versions as well.

Uri selectedImgFileUri = data.getData();

if (selectedImgFileUri == null ) {

    // The user has not selected any photo
}

try {

   InputStream input = mActivity.getContentResolver().openInputStream(selectedImgFileUri);
   mSelectedPhotoBmp = BitmapFactory.decodeStream(input);
}
catch (Throwable tr) {

    // Show message to try again
}
查看更多
爱死公子算了
6楼-- · 2018-12-31 04:02

I had the exact same problem. I need the filename so to be able to upload it to a website.

It worked for me, if I changed the intent to PICK. This was tested in AVD for Android 4.4 and in AVD for Android 2.1.

Add permission READ_EXTERNAL_STORAGE :

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

Change the Intent :

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

/* OLD CODE
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
  Intent.createChooser( intent, "Select Image" ),
  66453666
  );
*/

I did not have to change my code the get the actual path:

// Convert the image URI to the direct file system path of the image file
 public String mf_szGetRealPathFromURI(final Context context, final Uri ac_Uri )
 {
     String result = "";
     boolean isok = false;

     Cursor cursor = null;
      try { 
        String[] proj = { MediaStore.Images.Media.DATA };
        cursor = context.getContentResolver().query(ac_Uri,  proj, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        result = cursor.getString(column_index);
        isok = true;
      } finally {
        if (cursor != null) {
          cursor.close();
        }
      }

      return isok ? result : "";
 }
查看更多
栀子花@的思念
7楼-- · 2018-12-31 04:06

Note: This answer addresses part of the problem. For a complete solution (in the form of a library), look at Paul Burke's answer.

You could use the URI to obtain document id, and then query either MediaStore.Images.Media.EXTERNAL_CONTENT_URI or MediaStore.Images.Media.INTERNAL_CONTENT_URI (depending on the SD card situation).

To get document id:

// Will return "image:x*"
String wholeID = DocumentsContract.getDocumentId(uriThatYouCurrentlyHave);

// Split at colon, use second item in the array
String id = wholeID.split(":")[1];

String[] column = { MediaStore.Images.Media.DATA };     

// where id is equal to             
String sel = MediaStore.Images.Media._ID + "=?";

Cursor cursor = getContentResolver().
                          query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                          column, sel, new String[]{ id }, null);

String filePath = "";

int columnIndex = cursor.getColumnIndex(column[0]);

if (cursor.moveToFirst()) {
    filePath = cursor.getString(columnIndex);
}   

cursor.close();

Reference: I'm not able to find the post that this solution is taken from. I wanted to ask the original poster to contribute here. Will look some more tonight.

查看更多
登录 后发表回答