Wie extrahiere ich den Dateinamen aus dem von Intent.ACTION_GET_CONTENT zurückgegebenen URI?

98

Ich verwende den Dateimanager eines Drittanbieters, um eine Datei (in meinem Fall PDF) aus dem Dateisystem auszuwählen.

So starte ich die Aktivität:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(getString(R.string.app_pdf_mime_type));
intent.addCategory(Intent.CATEGORY_OPENABLE);

String chooserName = getString(R.string.Browse);
Intent chooser = Intent.createChooser(intent, chooserName);

startActivityForResult(chooser, ActivityRequests.BROWSE);

Das habe ich in onActivityResult:

Uri uri = data.getData();
if (uri != null) {
    if (uri.toString().startsWith("file:")) {
        fileName = uri.getPath();
    } else { // uri.startsWith("content:")

        Cursor c = getContentResolver().query(uri, null, null, null, null);

        if (c != null && c.moveToFirst()) {

            int id = c.getColumnIndex(Images.Media.DATA);
            if (id != -1) {
                fileName = c.getString(id);
            }
        }
    }
}

Das Code-Snippet stammt aus den Anweisungen des Open Intents File Managers , die hier verfügbar sind:
http://www.openintents.org/en/node/829

Der Zweck von if-elseist Abwärtskompatibilität. Ich frage mich, ob dies der beste Weg ist, um den Dateinamen zu erhalten, da ich festgestellt habe, dass andere Dateimanager alle möglichen Dinge zurückgeben.

Beispielsweise geben Documents ToGo Folgendes zurück:

content://com.dataviz.dxtg.documentprovider/document/file%3A%2F%2F%2Fsdcard%2Fdropbox%2FTransfer%2Fconsent.pdf

auf die getContentResolver().query()zurückkehrtnull .

Um die Sache interessanter zu machen, hat der unbenannte Dateimanager (ich habe diesen URI aus dem Client-Protokoll erhalten) Folgendes zurückgegeben:

/./sdcard/downloads/.bin


Gibt es eine bevorzugte Methode zum Extrahieren des Dateinamens aus dem URI oder sollte auf das Parsen von Zeichenfolgen zurückgegriffen werden?

Viktor Brešan
quelle
Gleiche Frage mit vielleicht besserer Antwort: stackoverflow.com/questions/8646246/…
Rémi

Antworten:

159

developer.android.com hat einen schönen Beispielcode dafür: https://developer.android.com/guide/topics/providers/document-provider.html

Eine komprimierte Version, um nur den Dateinamen zu extrahieren (vorausgesetzt, "dies" ist eine Aktivität):

public String getFileName(Uri uri) {
  String result = null;
  if (uri.getScheme().equals("content")) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    try {
      if (cursor != null && cursor.moveToFirst()) {
        result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
      }
    } finally {
      cursor.close();
    }
  }
  if (result == null) {
    result = uri.getPath();
    int cut = result.lastIndexOf('/');
    if (cut != -1) {
      result = result.substring(cut + 1);
    }
  }
  return result;
}
Stefan Haustein
quelle
Wenn Sie versuchen, mimeType oder fileName der Datei von der Kamera zu lesen, müssen Sie zuerst MediaScanner benachrichtigen, der den URI Ihrer gerade erstellten Datei von file://in content://in der onScanCompleted(String path, Uri uri)Methode stackoverflow.com/a/5815005/2163045
murt
10
Durch Hinzufügen new String[]{OpenableColumns.DISPLAY_NAME}eines zweiten Parameters zur Abfrage werden die Spalten für eine effizientere Anforderung gefiltert.
JM Lord
OpenableColumns.DISPLAY_NAMEhat bei mir nicht funktioniert, ich habe MediaStore.Files.FileColumns.TITLEstattdessen verwendet.
Dmitry Kopytov
Dies stürzt bei Videos ab, wenn der Cursor java.lang.IllegalArgumentException: Invalid column latitudeleider mit einem erstellt wird . Funktioniert aber perfekt für Fotos!
Lucas P.
44

Ich benutze so etwas:

String scheme = uri.getScheme();
if (scheme.equals("file")) {
    fileName = uri.getLastPathSegment();
}
else if (scheme.equals("content")) {
    String[] proj = { MediaStore.Images.Media.TITLE };
    Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
    if (cursor != null && cursor.getCount() != 0) {
        int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE);
        cursor.moveToFirst();
        fileName = cursor.getString(columnIndex);
    }
    if (cursor != null) {
        cursor.close();
    }
}
Ken Fehling
quelle
4
Ich habe einige Tests für Ihren Code durchgeführt. Bei einem vom OI-Dateimanager zurückgegebenen URI wird IllegalArgumentException ausgelöst, da die Spalte 'title' nicht vorhanden ist. Bei URI, die von Documents ToGo zurückgesetzt wurde, ist der Cursor null. Ein URI, der von einem unbekannten Dateimanager zurückgegeben wird, ist (offensichtlich) null.
Viktor Brešan
2
Hmm, interessant. Ja, das Testen auf Schema! = Null ist eine gute Idee. Eigentlich glaube ich nicht, dass TITEL das ist, was du willst. Ich habe das verwendet, weil bestimmte Medientypen in Android (wie Songs, die über die Musikauswahl ausgewählt wurden) URIs wie Inhalt haben: // media / external / audio / media / 78 und ich etwas Relevanteres als die ID-Nummer anzeigen wollte. Sie können einfach uri.getLastPathSegment () verwenden, wie mein Code es für file: // URIs verwendet, wenn Sie einen URI-ähnlichen Inhalt haben: //...somefile.pdf
Ken Fehling
1
uri.getLastPathSegment (); - Sie haben meinen Tag gerettet :)
Lumis
@ Ken Fehling: Das hat bei mir nicht funktioniert. Es funktioniert, wenn ich in einem Dateibrowser auf eine Datei klicke, aber wenn ich auf einen E-Mail-Anhang klicke, erhalte ich immer noch den Inhalt: // .... Ich habe alle Vorschläge hier ohne Glück ausprobiert. Irgendeine Idee warum?
Luis A. Florit
1
Hallo, warum cursorist das nicht close()d?
fikr4n
33

Entnommen aus dem Abrufen von Dateiinformationen | Android-Entwickler

Abrufen des Dateinamens.

private String queryName(ContentResolver resolver, Uri uri) {
    Cursor returnCursor =
            resolver.query(uri, null, null, null, null);
    assert returnCursor != null;
    int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
    returnCursor.moveToFirst();
    String name = returnCursor.getString(nameIndex);
    returnCursor.close();
    return name;
}
Bhargav
quelle
1
Ich habe viel zu lange danach gesucht!
Dasfima
7
Danke dir. Guter Herr, warum müssen sie so eine Kleinigkeit so nervig machen?
TylerJames
1
Es funktioniert nur mit Inhaltsdateien. Siehe stackoverflow.com/a/25005243/6325722 für die vollständige Methode
Johnny Five
17

Wenn Sie es kurz haben möchten, sollte dies funktionieren.

Uri uri= data.getData();
File file= new File(uri.getPath());
file.getName();
Samuel
quelle
1
Ich erhalte einen Namen mit file.getName (), aber sein nicht tatsächlicher Name
Tushar Thakur
1
Mein Dateiname ist user.jpg, aber ich erhalte "6550" als Antwort
Tushar Thakur
1
Der tatsächliche Dateiname wird nicht zurückgegeben. Tatsächlich wird der letzte Teil Ihrer Ressourcen-URL zurückgegeben.
Emdadul Sawon
Dies funktioniert nicht als Datei! Meist müssen URIs heute vom MediaStore dekodiert werden. Grund, warum ich und andere die Zahlen bekommen. Weil das die IDs dieser Dateien sind.
sud007
Dies war eine Antwort vor 4 Jahren. Android ändert offensichtlich jede Version.
Samuel
17

Einfachste Möglichkeiten, um den Dateinamen zu erhalten:

val fileName = File(uri.path).name
// or
val fileName = uri.pathSegments.last()

Wenn sie Ihnen nicht den richtigen Namen geben, sollten Sie Folgendes verwenden:

fun Uri.getName(context: Context): String {
    val returnCursor = context.contentResolver.query(this, null, null, null, null)
    val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
    returnCursor.moveToFirst()
    val fileName = returnCursor.getString(nameIndex)
    returnCursor.close()
    return fileName
}
Metu
quelle
7

Ich verwende den folgenden Code, um den Dateinamen und die Dateigröße von Uri in meinem Projekt abzurufen.

/**
 * Used to get file detail from uri.
 * <p>
 * 1. Used to get file detail (name & size) from uri.
 * 2. Getting file details from uri is different for different uri scheme,
 * 2.a. For "File Uri Scheme" - We will get file from uri & then get its details.
 * 2.b. For "Content Uri Scheme" - We will get the file details by querying content resolver.
 *
 * @param uri Uri.
 * @return file detail.
 */
public static FileDetail getFileDetailFromUri(final Context context, final Uri uri) {
    FileDetail fileDetail = null;
    if (uri != null) {
        fileDetail = new FileDetail();
        // File Scheme.
        if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
            File file = new File(uri.getPath());
            fileDetail.fileName = file.getName();
            fileDetail.fileSize = file.length();
        }
        // 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);
                int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                fileDetail.fileName = returnCursor.getString(nameIndex);
                fileDetail.fileSize = returnCursor.getLong(sizeIndex);
                returnCursor.close();
            }
        }
    }
    return fileDetail;
}

/**
 * File Detail.
 * <p>
 * 1. Model used to hold file details.
 */
public static class FileDetail {

    // fileSize.
    public String fileName;

    // fileSize in bytes.
    public long fileSize;

    /**
     * Constructor.
     */
    public FileDetail() {

    }
}
Vasanth
quelle
Hat geholfen, mein Problem beim Laden von Dateinamen aus externen oder internen Daten zu lösen! Sehr nützlich, danke!
Brandon
4
public String getFilename() 
{
/*  Intent intent = getIntent();
    String name = intent.getData().getLastPathSegment();
    return name;*/
    Uri uri=getIntent().getData();
    String fileName = null;
    Context context=getApplicationContext();
    String scheme = uri.getScheme();
    if (scheme.equals("file")) {
        fileName = uri.getLastPathSegment();
    }
    else if (scheme.equals("content")) {
        String[] proj = { MediaStore.Video.Media.TITLE };
        Uri contentUri = null;
        Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
        if (cursor != null && cursor.getCount() != 0) {
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.TITLE);
            cursor.moveToFirst();
            fileName = cursor.getString(columnIndex);
        }
    }
    return fileName;
}
Sujith Manjavana
quelle
Das hat in meinem Fall nicht funktioniert, aber die Antwort von Vasanth hat funktioniert.
Golnar
4

Für Kotlin können Sie Folgendes verwenden:

object FileUtils {

   fun Context.getFileName(uri: Uri): String?
        = when (uri.scheme) {
            ContentResolver.SCHEME_FILE -> File(uri.path).name
            ContentResolver.SCHEME_CONTENT -> getCursorContent(uri)
            else -> null
        }

    private fun Context.getCursorContent(uri: Uri): String? 
        = try {
            contentResolver.query(uri, null, null, null, null)?.let { cursor ->
                cursor.run {
                    if (moveToFirst()) getString(getColumnIndex(OpenableColumns.DISPLAY_NAME))
                    else null
                }.also { cursor.close() }
            }
        } catch (e : Exception) { null }
Tamim Attafi
quelle
Gute Antwort, aber meiner Meinung nach ist es besser, diese Funs (Context.getFileName und Context.getCursorContent) in eine Datei mit dem Namen 'Context' einzufügen, das Wort FileUtils zu löschen und es als Erweiterung zu verwenden, zum Beispiel: val txtFileName = context.getFileName (uri)
Alexey Simchenko
@ LumisD Sie können diese Erweiterungen in jede Datei importieren, Ihr Vorschlag ist korrekt, aber ich hasse es nur, wenn Methoden global sind. Ich möchte bestimmte Methoden nur sehen, wenn ich sie importiere und nicht immer. Und manchmal bevorzuge ich es, dieselben Methodennamen und unterschiedliche Quellen zu haben, damit ich die benötigten an der richtigen Stelle importiere :)
Tamim Attafi
Sie können es folgendermaßen importieren: importieren Sie com.example.FileUtils.getFileName oder schreiben Sie einfach context.getFileName (uri) und drücken Sie Alt + Enter, und Ihre IDE
erledigt
3

Die am meisten komprimierte Version:

public String getNameFromURI(Uri uri) {
    Cursor c = getContentResolver().query(uri, null, null, null, null);
    c.moveToFirst();
    return c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
artdeell
quelle
1
Vergessen Sie nicht, den Cursor zu schließen :)
BekaBot
2
String Fpath = getPath(this, uri) ;
File file = new File(Fpath);
String filename = file.getName();
user5233139
quelle
4
Können Sie uns etwas erklären, was Ihr Code bewirkt? Auf diese Weise ist Ihre Antwort für andere Benutzer nützlicher, die über dieses Problem stolpern. Danke
wmk
Aus irgendeinem Grund funktioniert dies bei Marshmellow nicht. Es gibt nur "Audio und eine Nummer" aus
Alex
1

Meine Version der Antwort ist der von @Stefan Haustein sehr ähnlich. Ich habe die Antwort auf der Android-Entwicklerseite gefunden. Abrufen von Dateiinformationen ; Die Informationen hier sind zu diesem speziellen Thema noch komprimierter als auf der Website des Storage Access Framework- Handbuchs. Im Ergebnis der Abfrage lautet der Spaltenindex mit dem Dateinamen OpenableColumns.DISPLAY_NAME. Keine der anderen Antworten / Lösungen für Spaltenindizes hat bei mir funktioniert. Unten ist die Beispielfunktion:

 /**
 * @param uri uri of file.
 * @param contentResolver access to server app.
 * @return the name of the file.
 */
def extractFileName(uri: Uri, contentResolver: ContentResolver): Option[String] = {

    var fileName: Option[String] = None
    if (uri.getScheme.equals("file")) {

        fileName = Option(uri.getLastPathSegment)
    } else if (uri.getScheme.equals("content")) {

        var cursor: Cursor = null
        try {

            // Query the server app to get the file's display name and size.
            cursor = contentResolver.query(uri, null, null, null, null)

            // Get the column indexes of the data in the Cursor,
            // move to the first row in the Cursor, get the data.
            if (cursor != null && cursor.moveToFirst()) {

                val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                fileName = Option(cursor.getString(nameIndex))
            }

        } finally {

            if (cursor != null) {
                cursor.close()
            }

        }

    }

    fileName
}
RenatoIvancic
quelle
1

Zuerst müssen Sie Ihr URIObjekt in ein URLObjekt konvertieren und dann mit FileObjekt einen Dateinamen abrufen:

try
    {
        URL videoUrl = uri.toURL();
        File tempFile = new File(videoUrl.getFile());
        String fileName = tempFile.getName();
    }
    catch (Exception e)
    {

    }

Das war's, sehr einfach.

Ayaz Alifov
quelle
1

Stefan Haustein Funktion für Xamarin / c #:

public string GetFilenameFromURI(Android.Net.Uri uri)
        {
            string result = null;
            if (uri.Scheme == "content")
            {
                using (var cursor = Application.Context.ContentResolver.Query(uri, null, null, null, null))
                {
                    try
                    {
                        if (cursor != null && cursor.MoveToFirst())
                        {
                            result = cursor.GetString(cursor.GetColumnIndex(OpenableColumns.DisplayName));
                        }
                    }
                    finally
                    {
                        cursor.Close();
                    }
                }
            }
            if (result == null)
            {
                result = uri.Path;
                int cut = result.LastIndexOf('/');
                if (cut != -1)
                {
                    result = result.Substring(cut + 1);
                }
            }
            return result;
        }
Stefan Michev
quelle
1

Wenn Sie den Dateinamen mit der Erweiterung haben möchten, verwende ich diese Funktion, um ihn abzurufen. Es funktioniert auch mit Google Drive File Picks

public static String getFileName(Uri uri) {
    String result;

    //if uri is content
    if (uri.getScheme() != null && uri.getScheme().equals("content")) {
        Cursor cursor = global.getInstance().context.getContentResolver().query(uri, null, null, null, null);
        try {
            if (cursor != null && cursor.moveToFirst()) {
                //local filesystem
                int index = cursor.getColumnIndex("_data");
                if(index == -1)
                    //google drive
                    index = cursor.getColumnIndex("_display_name");
                result = cursor.getString(index);
                if(result != null)
                    uri = Uri.parse(result);
                else
                    return null;
            }
        } finally {
            cursor.close();
        }
    }

    result = uri.getPath();

    //get filename + ext of path
    int cut = result.lastIndexOf('/');
    if (cut != -1)
        result = result.substring(cut + 1);
    return result;
}
save_jeff
quelle
1

Das hat bei mir tatsächlich funktioniert:

private String uri2filename() {

    String ret;
    String scheme = uri.getScheme();

    if (scheme.equals("file")) {
        ret = uri.getLastPathSegment();
    }
    else if (scheme.equals("content")) {
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            ret = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
        }
   }
   return ret;
}
Jose Manuel Gomez Alvarez
quelle
0

Bitte versuchen Sie dies:

  private String displayName(Uri uri) {

             Cursor mCursor =
                     getApplicationContext().getContentResolver().query(uri, null, null, null, null);
             int indexedname = mCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
             mCursor.moveToFirst();
             String filename = mCursor.getString(indexedname);
             mCursor.close();
             return filename;
 }
Mamour Ndiaye
quelle
0

Kombination aller Antworten

Folgendes habe ich erreicht, nachdem ich alle hier vorgestellten Antworten gelesen habe und was einige Airgram in ihren SDKs getan haben - ein Dienstprogramm, das ich auf Github als Open-Source-Version bereitgestellt habe:

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

Verwendung

So einfach wie anrufen , UriUtils.getDisplayNameSize(). Es gibt sowohl den Namen als auch die Größe des Inhalts an.

Hinweis: Funktioniert nur mit einem Inhalt: // Uri

Hier ist ein Blick auf den Code:

/**
 * References:
 * - https://www.programcreek.com/java-api-examples/?code=MLNO/airgram/airgram-master/TMessagesProj/src/main/java/ir/hamzad/telegram/MediaController.java
 * - /programming/5568874/how-to-extract-the-file-name-from-uri-returned-from-intent-action-get-content
 *
 * @author [email protected]/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
quelle