Comment extraire le nom de fichier de l'URI renvoyé par Intent.ACTION_GET_CONTENT?


98

J'utilise un gestionnaire de fichiers tiers pour sélectionner un fichier (PDF dans mon cas) dans le système de fichiers.

Voici comment je lance l'activité:

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

Voici ce que j'ai dans 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);
            }
        }
    }
}

L'extrait de code est emprunté aux instructions d' Open Intents File Manager disponibles ici:
http://www.openintents.org/en/node/829

Le but de if-elseest la rétrocompatibilité. Je me demande si c'est le meilleur moyen d'obtenir le nom du fichier car j'ai constaté que d'autres gestionnaires de fichiers renvoient toutes sortes de choses.

Par exemple, Documents ToGo renvoie quelque chose comme ce qui suit:

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

sur quels getContentResolver().query()retours null.

Pour rendre les choses plus intéressantes, le gestionnaire de fichiers sans nom (j'ai obtenu cet URI du journal client) a renvoyé quelque chose comme:

/./sdcard/downloads/.bin


Existe-t-il un moyen préféré d'extraire le nom de fichier de l'URI ou doit-on recourir à l'analyse de chaînes?


Même question avec peut-être une meilleure réponse: stackoverflow.com/questions/8646246/…
Rémi

Réponses:


160

developer.android.com a un bon exemple de code pour cela: https://developer.android.com/guide/topics/providers/document-provider.html

Une version condensée pour extraire simplement le nom du fichier (en supposant que "ceci" est une activité):

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

au cas où vous tenteriez de lire mimeType ou fileName du fichier de la caméra, vous devez d'abord informer MediaScanner qui convertira l' URI de votre fichier que vous venez de créer de file://à content://dans la onScanCompleted(String path, Uri uri)méthode stackoverflow.com/a/5815005/2163045
murt

10
L'ajout en new String[]{OpenableColumns.DISPLAY_NAME}tant que deuxième paramètre à la requête filtrera les colonnes pour une requête plus efficace.
JM Lord

OpenableColumns.DISPLAY_NAMEn'a pas fonctionné pour moi, j'ai utilisé à la MediaStore.Files.FileColumns.TITLEplace.
Dmitry Kopytov le

Cela plantera pour les vidéos lors de la création du curseur avec un java.lang.IllegalArgumentException: Invalid column latitudemalheureusement. Fonctionne parfaitement pour les photos cependant!
Lucas P.

44

J'utilise quelque chose comme ça:

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
J'ai exécuté quelques tests sur votre code. Sur l'URI renvoyé par le gestionnaire de fichiers OI, une exception IllegalArgumentException est lancée car la colonne 'title' n'existe pas. Sur l'URI retourné par Documents, le curseur ToGo est nul. Sur l'URI renvoyé par un schéma de gestionnaire de fichiers inconnu, il est (évidemment) nul.
Viktor Brešan

2
Hum ... intéressant. Ouais, tester le schéma! = Null est une bonne idée. En fait, je ne pense pas que TITLE soit ce que vous voulez. J'utilisais cela parce que certains types de médias sous Android (comme les chansons choisies via le sélecteur de musique) ont des URI comme content: // media / external / audio / media / 78 et je voulais afficher quelque chose de plus pertinent que le numéro d'identification. Vous pouvez simplement utiliser uri.getLastPathSegment () comme mon code utilise pour file: // URIs si vous avez un URI comme content: //...somefile.pdf
Ken Fehling

1
uri.getLastPathSegment (); - vous avez sauvé ma journée :)
Lumis

@Ken Fehling: Cela n'a pas fonctionné pour moi. Cela fonctionne lorsque je clique sur un fichier dans un navigateur de fichiers, mais lorsque je clique sur une pièce jointe à un e-mail, j'obtiens toujours le contenu: // ... chose. J'ai essayé toutes les suggestions ici sans chance. Une idée pourquoi?
Luis A. Florit

1
Salut, pourquoi le cursorn'est pas close()d?
fikr4n

33

Tiré de la récupération des informations du fichier | Développeurs Android

Récupération du nom d'un fichier.

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
Je cherche ça depuis trop longtemps!
dasfima

7
Je vous remercie. Bon seigneur, pourquoi doivent-ils rendre une chose aussi insignifiante si ennuyeuse?
TylerJames

1
Cela ne fonctionne qu'avec les fichiers de contenu. Voir stackoverflow.com/a/25005243/6325722 pour la méthode complète
Johnny Five

17

Si vous le voulez court, cela devrait fonctionner.

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

1
Je reçois un nom avec file.getName () mais ce n'est pas le nom réel
Tushar Thakur

1
Mon nom de fichier est user.jpg mais j'obtiens "6550" en réponse
Tushar Thakur

1
Il ne renvoie pas le nom de fichier réel. En fait, il renvoie la dernière partie de votre URL de ressource.
Emdadul Sawon

Cela ne fonctionne pas comme un fichier! La plupart des URI doivent être décodés par le MediaStore aujourd'hui. Raison pour laquelle moi et d'autres obtenons les chiffres. Parce que ce sont les ID de ces fichiers.
sud007

C'était une réponse donnée il y a 4 ans. Android change évidemment chaque version.
Samuel

17

Moyens les plus simples pour obtenir le nom du fichier:

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

S'ils ne vous donnent pas le bon nom, vous devez utiliser:

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

J'utilise le code ci-dessous pour obtenir le nom du fichier et la taille du fichier à partir d'Uri dans mon projet.

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

    }
}

A aidé à résoudre mon problème avec le chargement des noms de fichiers à partir de données externes ou internes! Très utile merci!
Brandon

5

Pour Kotlin, vous pouvez utiliser quelque chose comme ceci:

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 }

Bonne réponse, mais à mon avis, il vaut mieux insérer ces funs (Context.getFileName et Context.getCursorContent) dans un fichier avec le nom 'Context', supprimer les mots objet FileUtils et l'utiliser comme extension, par exemple: val txtFileName = context.getFileName (uri)
Alexey Simchenko

@LumisD vous pouvez importer ces extensions dans n'importe quel fichier, votre suggestion est correcte mais je déteste juste quand les méthodes sont globales. Je veux voir certaines méthodes uniquement si je les importe et pas toujours. Et parfois, je préfère avoir les mêmes noms de méthodes et des sources différentes, alors
j'importe

Vous pouvez l'importer de cette façon: importez com.example.FileUtils.getFileName ou écrivez simplement context.getFileName (uri) et appuyez sur Alt + Entrée et votre IDE le fera pour vous :)
Tamim Attafi

5

La version la plus condensée:

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
n'oubliez pas de fermer le curseur :)
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;
}

cela n'a pas fonctionné dans mon cas, mais la réponse de Vasanth a fonctionné.
Nabzi le

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

4
Pouvez-vous expliquer un peu ce que fait votre code? De cette façon, votre réponse sera plus utile pour les autres utilisateurs qui trébuchent sur ce problème. Merci
wmk

pour une raison quelconque, cela ne fonctionne pas sur le marshmellow. Il sort juste "audio et un certain nombre"
Alex

1

Ma version de la réponse est en fait très similaire à la @Stefan Haustein. J'ai trouvé la réponse sur la page des développeurs Android Récupération des informations sur les fichiers ; les informations ici sont encore plus condensées sur ce sujet spécifique que sur le site du guide Storage Access Framework . Dans le résultat de la requête, l'index de colonne contenant le nom de fichier est OpenableColumns.DISPLAY_NAME. Aucune des autres réponses / solutions pour les index de colonnes n'a fonctionné pour moi. Voici l'exemple de fonction:

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

Tout d'abord, vous devez convertir votre URIobjet en URLobjet, puis utiliser Fileobject pour récupérer un nom de fichier:

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

    }

C'est tout, très facile.


1

Fonction Stefan Haustein pour 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

Si vous voulez avoir le nom de fichier avec l'extension, j'utilise cette fonction pour l'obtenir. Il fonctionne également avec les sélections de fichiers 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

Cela a fonctionné pour moi:

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

Veuillez essayer ceci:

  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

Combinaison de toutes les réponses

Voici ce à quoi je suis arrivé après avoir lu toutes les réponses présentées ici ainsi que ce que certains Airgram ont fait dans leurs SDK - Un utilitaire que j'ai open source sur Github:

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

Usage

Aussi simple que d' appeler, UriUtils.getDisplayNameSize(). Il fournit à la fois le nom et la taille du contenu.

Remarque: fonctionne uniquement avec un contenu: // Uri

Voici un aperçu du 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 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;
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.