Come estrarre il nome del file dall'URI restituito da Intent.ACTION_GET_CONTENT?


98

Sto utilizzando un file manager di terze parti per scegliere un file (PDF nel mio caso) dal file system.

Ecco come avvio l'attività:

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

Questo è quello che ho 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);
            }
        }
    }
}

Lo snippet di codice è preso in prestito dalle istruzioni di Open Intents File Manager disponibili qui:
http://www.openintents.org/en/node/829

Lo scopo di if-elseè la compatibilità con le versioni precedenti. Mi chiedo se questo sia il modo migliore per ottenere il nome del file poiché ho scoperto che altri gestori di file restituiscono ogni genere di cose.

Ad esempio, Documents ToGo restituisce qualcosa di simile al seguente:

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

su cui getContentResolver().query()ritorna null.

Per rendere le cose più interessanti, il file manager senza nome (ho ottenuto questo URI dal registro del client) ha restituito qualcosa del tipo:

/./sdcard/downloads/.bin


Esiste un modo preferito per estrarre il nome del file dall'URI o si dovrebbe ricorrere all'analisi delle stringhe?


Stessa domanda con una risposta forse migliore: stackoverflow.com/questions/8646246/…
Rémi

Risposte:


160

developer.android.com ha un bel codice di esempio per questo: https://developer.android.com/guide/topics/providers/document-provider.html

Una versione ridotta per estrarre solo il nome del file (supponendo che "questo" sia un'attività):

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

nel caso in cui si tenta leggere mimeType o il nome del file da fotocamera, in primo luogo si deve informare MediaScanner che convertirà URI del file appena creato da file://a content://in onScanCompleted(String path, Uri uri)modo stackoverflow.com/a/5815005/2163045
Murt

10
L'aggiunta new String[]{OpenableColumns.DISPLAY_NAME}come secondo parametro alla query filtrerà le colonne per una richiesta più efficiente.
JM Lord

OpenableColumns.DISPLAY_NAMEnon ha funzionato per me, ho usato MediaStore.Files.FileColumns.TITLEinvece.
Dmitry Kopytov,

Questo andrà in crash per i video quando si crea il cursore con un java.lang.IllegalArgumentException: Invalid column latitude. Funziona perfettamente per le foto però!
Lucas P.

44

Sto usando qualcosa del genere:

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

4
Ho eseguito alcuni test sul tuo codice. Sull'URI restituito da OI File Manager Viene generata un'eccezione IllegalArgumentException poiché la colonna "titolo" non esiste. Su URI ripristinato da Documents ToGo il cursore è nullo. Sull'URI restituito dallo schema del file manager sconosciuto è (ovviamente) nullo.
Viktor Brešan

2
Hmm, interessante. Sì, testare lo schema! = Null è una buona idea. In realtà non penso che TITLE sia quello che vuoi. Lo stavo usando perché alcuni tipi di media in Android (come i brani scelti tramite il selettore di musica) hanno URI come content: // media / external / audio / media / 78 e volevo visualizzare qualcosa di più rilevante del numero ID. Puoi semplicemente usare uri.getLastPathSegment () come il mio codice usa per file: // URI se hai un URI come il contenuto: //...somefile.pdf
Ken Fehling

1
uri.getLastPathSegment (); - mi hai salvato la giornata :)
Lumis

@Ken Fehling: questo non ha funzionato per me. Funziona quando faccio clic su un file in un browser di file, ma quando faccio clic su un allegato di posta elettronica, ottengo comunque il contenuto: // ... cosa. Ho provato tutti i suggerimenti qui senza fortuna. Qualche idea sul perché?
Luis A. Florit

1
Ciao, perché cursornon è close()d?
fikr4n

33

Tratto da Retrieving File information | Sviluppatori Android

Recupero del nome di un file.

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

1
Lo stavo cercando da troppo tempo!
dasfima

7
Grazie. Buon Dio, perché devono rendere una cosa così banale così fastidiosa?
TylerJames

1
Funziona solo con i file di contenuto. Vedi stackoverflow.com/a/25005243/6325722 per il metodo completo
Johnny Five,

17

Se lo vuoi corto dovrebbe funzionare.

Uri uri= data.getData();
File file= new File(uri.getPath());
file.getName();

1
Ricevo un nome con file.getName () ma non è il nome effettivo
Tushar Thakur

1
Il mio fileName è user.jpg ma ricevo "6550" in risposta
Tushar Thakur

1
Non restituisce il nome del file effettivo. In realtà restituisce l'ultima parte dell'URL della tua risorsa.
Emdadul Sawon

Questo non funziona come file! La maggior parte degli URI devono essere decodificati da MediaStore oggi. Motivo per cui io e altri stiamo ottenendo i numeri. Perché quelli sono gli ID di quei file.
sud007

Questa è stata una risposta data 4 anni fa. Android sta ovviamente cambiando ogni versione.
Samuel il

17

Modi più semplici per ottenere il nome del file:

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

Se non ti danno il nome giusto dovresti usare:

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
}

7

Uso il codice seguente per ottenere il nome del file e la dimensione del file da Uri nel mio progetto.

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

    }
}

Mi ha aiutato a risolvere il mio problema con il caricamento dei nomi dei file da dati esterni o interni! Molto utile grazie!
Brandon

5

Per Kotlin, puoi usare qualcosa del genere:

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 }

Buona risposta, ma a mio avviso è meglio inserire questi divertenti (Context.getFileName e Context.getCursorContent) in un file con nome 'Context', eliminare le parole oggetto FileUtils e usarlo come estensione, ad esempio: val txtFileName = context.getFileName (uri)
Alexey Simchenko

@LumisD puoi importare queste estensioni in qualsiasi file, il tuo suggerimento è corretto ma odio solo quando i metodi sono globali. Voglio vedere alcuni metodi solo se li importi e non sempre. E a volte preferisco avere gli stessi nomi di metodo e fonti diverse, quindi importa quelli di cui ho bisogno nel posto giusto :)
Tamim Attafi

Puoi importarlo in questo modo: import com.example.FileUtils.getFileName o semplicemente scrivi context.getFileName (uri) e premi Alt + Invio e il tuo IDE lo farà per te :)
Tamim Attafi

5

La versione più condensata:

public String getNameFromURI(Uri uri) {
    Cursor c = getContentResolver().query(uri, null, null, null, null);
    c.moveToFirst();
    return c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}

1
non dimenticare di chiudere il cursore :)
BekaBot

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

questo non ha funzionato nel mio caso, ma la risposta di Vasanth sì.
Nabzi

2
String Fpath = getPath(this, uri) ;
File file = new File(Fpath);
String filename = file.getName();

4
Potresti spiegare un po 'cosa fa il tuo codice? In questo modo la tua risposta sarà più utile per altri utenti che si imbattono in questo problema. Grazie
wmk

per qualche motivo questo non funziona su marshmellow. Emette solo "audio e qualche numero"
Alex

1

La mia versione della risposta è in realtà molto simile a @Stefan Haustein. Ho trovato la risposta nella pagina degli sviluppatori Android Recupero delle informazioni sui file ; le informazioni qui sono ancora più condensate su questo argomento specifico che sul sito della guida di Storage Access Framework . Nel risultato della query l'indice della colonna contenente il nome del file è OpenableColumns.DISPLAY_NAME. Nessuna delle altre risposte / soluzioni per gli indici di colonna ha funzionato per me. Di seguito è riportata la funzione di esempio:

 /**
 * @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
}

1

Innanzitutto, devi convertire il tuo URIoggetto in URLoggetto, quindi utilizzare Fileobject per recuperare un nome file:

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

    }

È tutto molto semplice.


1

Funzione di Stefan Haustein per 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;
        }

1

Se vuoi avere il nome del file con estensione, uso questa funzione per ottenerlo. Funziona anche con i file pick di Google Drive

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

1

Questo in realtà ha funzionato per me:

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

0

Per favore prova questo:

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

0

Combinazione di tutte le risposte

Ecco a cosa sono arrivato dopo aver letto tutte le risposte presentate qui e ciò che alcuni Airgram hanno fatto nei loro SDK - Un'utilità che ho reso open source su Github:

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

Utilizzo

Semplice come chiamare, UriUtils.getDisplayNameSize() . Fornisce sia il nome che la dimensione del contenuto.

Nota: funziona solo con un contenuto: // Uri

Ecco uno sguardo al codice:

/**
 * 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 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;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.