25

I call startActivityForResult with Intent ACTION_GET_CONTENT. Some app returns me data with this Uri:

content://media/external/images/media/18122

I don't know if it is image or video or some custom content. How do I use ContentResolver to get the actual file name or content title from this Uri?

Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
Pointer Null
  • 39,597
  • 13
  • 90
  • 111
  • Be aware that there might not *be* a file. The content could be coming from Google Drive, or maybe some other source that's generating the content on the fly. Maybe you want to use `getContentResolver().openInputStream(uri)` instead. – Edward Falk Mar 31 '16 at 01:23
  • Certainly. The name was intented only for display purpose. – Pointer Null Apr 01 '16 at 11:52

7 Answers7

23

@Durairaj's answer is specific to getting the path of a file. If what you're searching for is the file's actual name (since you should be using Content Resolution, at which point you'll probably get a lot of content:// URIs) you'll need to do the following:

(Code copied from Durairaj's answer and modified)

        String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
        Cursor metaCursor = cr.query(uri, projection, null, null, null);
        if (metaCursor != null) {
            try {
                if (metaCursor.moveToFirst()) {
                    fileName = metaCursor.getString(0);
                }
            } finally {
                metaCursor.close();
            }
        }

The main piece to note here is that we're using MediaStore.MediaColumns.DISPLAY_NAME, which returns the actual name of the content. You might also try MediaStore.MediaColumns.TITLE, as I'm not sure what the difference is.

Navarr
  • 3,703
  • 7
  • 33
  • 57
  • I'm going to guess that `MediaStore.MediaColumns.DISPLAY_NAME` is only valid for `content://media/` uris. – Edward Falk Mar 31 '16 at 01:47
  • 1
    @EdwardFalk that's not true, that constant is just an inlined alias to `OpenableColumns.DISPLAY_NAME` which is well documented at [Retrieving File Information](http://developer.android.com/training/secure-file-sharing/retrieve-info.html#RetrieveFileInfo) and reasonable `ContentProvider`s should handle that column: *"Providers that serve openable URIs must support at least these columns when queried."* from [`OpenableColumns`](http://developer.android.com/reference/android/provider/OpenableColumns.html). – TWiStErRob Apr 02 '16 at 20:46
  • Good point. What constitutes an "openable" Uri? Does it include "file://" ? – Edward Falk Apr 03 '16 at 16:13
19

You can get file name from this code, or any other field by modifying the projection

String[] projection = {MediaStore.MediaColumns.DATA};

ContentResolver cr = getApplicationContext().getContentResolver();
Cursor metaCursor = cr.query(uri, projection, null, null, null);
if (metaCursor != null) {
    try {
        if (metaCursor.moveToFirst()) {
            path = metaCursor.getString(0);
        }
    } finally {
        metaCursor.close();
    }
}
return path;
Edward Falk
  • 9,991
  • 11
  • 77
  • 112
Durairaj Packirisamy
  • 4,635
  • 1
  • 21
  • 27
  • 3
    And if I don't know the projection? e.g. get an intent in my app that can save ANY file – ozba Apr 17 '13 at 10:34
  • @Durairaj P: This doesn't work for me. It works if I click a file in a filemanager, but if I click an attachment in an email I still get the `content://...` string. Any clues? – Luis A. Florit May 04 '13 at 21:07
  • @LuisA.Florit You probably don't have this problem anymore, but I posted a new answer a little lower down on the question for getting the file's name instead of it's path. http://stackoverflow.com/a/23270545/43790 – Navarr Apr 24 '14 at 13:44
17

To get filename, you can use new DocumentFile format.

DocumentFile documentFile = DocumentFile.fromSingleUri(this, data.getdata());
String fileName = documentFile.getName();
cooldev
  • 419
  • 1
  • 4
  • 13
3

For anyone using Kotlin who has the same problem, you can define an extension method to get the file name and size (in bytes) in one fell swoop. If it is unable to retrieve the fields, it returns null.

fun Uri.contentSchemeNameAndSize(): Pair<String, Int>? {
    return contentResolver.query(this, null, null, null, null)?.use { cursor ->
        if (!cursor.moveToFirst()) return@use null

        val name = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
        val size = cursor.getColumnIndex(OpenableColumns.SIZE)

        cursor.getString(name) to cursor.getInt(size)
    }
}

Use it thusly

val nameAndSize = yourUri.contentNameAndSize()
// once you've confirmed that is not null, you can then do
val (name, size) = nameAndSize

It might throw an exception, but it hasn't ever done so for me (as long as the URI is a valid content:// URI).

Leo Aso
  • 11,898
  • 3
  • 25
  • 46
0
private static String getRealPathFromURI(Context context, Uri contentUri)
{
    String[] proj = { MediaStore.Images.Media.DATA };
    CursorLoader loader = new CursorLoader(context, contentUri, proj, null, null, null);
    Cursor cursor = loader.loadInBackground();
    int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    String result = cursor.getString(column_index);
    cursor.close();
    return result;
}
Robert
  • 5,278
  • 43
  • 65
  • 115
Satyawan Hajare
  • 1,112
  • 11
  • 10
0

The accepted answer is not complete. There are more checks missed out.

Here is what I have arrived at after a read of all the answers presented here as well what some Airgram has done in their SDKs - A utility that I have open sourced on Github:

https://github.com/mankum93/UriUtilsAndroid/tree/master/app/src/main/java/com/androiduriutils

Usage

As simple as calling, UriUtils.getDisplayNameSize(). It provides both the name and size of the content.

Note: Only works with a content:// Uri

Here is a glimpse on the code:

/**
 * References:
 * - https://www.programcreek.com/java-api-examples/?code=MLNO/airgram/airgram-master/TMessagesProj/src/main/java/ir/hamzad/telegram/MediaController.java
 * - https://stackoverflow.com/questions/5568874/how-to-extract-the-file-name-from-uri-returned-from-intent-action-get-content
 *
 * @author Manish@bit.ly/2HjxA0C
 * Created on: 03-07-2020
 */
public final class UriUtils {


    public static final int CONTENT_SIZE_INVALID = -1;

    /**
     * @param context context
     * @param contentUri content Uri, i.e, of the scheme <code>content://</code>
     * @return The Display name and size for content. In case of non-determination, display name
     * would be null and content size would be {@link #CONTENT_SIZE_INVALID}
     */
    @NonNull
    public static DisplayNameAndSize getDisplayNameSize(@NonNull Context context, @NonNull Uri contentUri){

        final String scheme = contentUri.getScheme();
        if(scheme == null || !scheme.equals(ContentResolver.SCHEME_CONTENT)){
            throw new RuntimeException("Only scheme content:// is accepted");
        }

        final DisplayNameAndSize displayNameAndSize = new DisplayNameAndSize();
        displayNameAndSize.size = CONTENT_SIZE_INVALID;

        String[] projection = new String[]{MediaStore.Images.Media.DATA, OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
        Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {

                // Try extracting content size

                int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
                if (sizeIndex != -1) {
                    displayNameAndSize.size = cursor.getLong(sizeIndex);
                }

                // Try extracting display name
                String name = null;

                // Strategy: The column name is NOT guaranteed to be indexed by DISPLAY_NAME
                // so, we try two methods
                int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                if (nameIndex != -1) {
                    name = cursor.getString(nameIndex);
                }

                if (nameIndex == -1 || name == null) {
                    nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
                    if (nameIndex != -1) {
                        name = cursor.getString(nameIndex);
                    }
                }
                displayNameAndSize.displayName = name;
            }
        }
        finally {
            if(cursor != null){
                cursor.close();
            }
        }

        // We tried querying the ContentResolver...didn't work out
        // Try extracting the last path segment
        if(displayNameAndSize.displayName == null){
            displayNameAndSize.displayName = contentUri.getLastPathSegment();
        }

        return displayNameAndSize;
    }
}
Manish Kumar Sharma
  • 12,982
  • 9
  • 58
  • 105
-1

You can use the solution proposed by Durairaj with the following as the projection array:

String[] projection = { "_data" };
Ander
  • 209
  • 1
  • 5