web-dev-qa-db-fra.com

Obtenir le chemin d'accès réel depuis l'URI, Android le nouveau cadre d'accès au stockage KitKat

Avant l’accès à la nouvelle galerie dans Android 4.4 (KitKat), j’avais mon vrai chemin sur la carte SD avec cette méthode:

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

Maintenant, Intent.ACTION_GET_CONTENT renvoie différentes données:

Avant:

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

Maintenant:

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

Comment pourrais-je réussir à obtenir le vrai chemin sur la carte SD?

196
Álvaro

Remarque: cette réponse résout une partie du problème. Pour une solution complète (sous la forme d'une bibliothèque), regardez réponse de Paul Burke .

Vous pouvez utiliser l'URI pour obtenir document id, puis interroger MediaStore.Images.Media.EXTERNAL_CONTENT_URI ou MediaStore.Images.Media.INTERNAL_CONTENT_URI (selon la situation de la carte SD).

Pour obtenir l'identifiant du document:

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

Référence: Je ne parviens pas à trouver le message d'où provient cette solution. Je voulais demander à l'affiche originale de contribuer ici. Va chercher plus ce soir.

117
Vikram

Le chemin d'accès au fichier provient de MediaProvider, DownloadsProvider et ExternalStorageProvider, tout en utilisant la méthode non officielle ContentProvider que vous avez mentionnée.

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

Ceux-ci sont tirés de ma bibliothèque open source, aFileChooser .

495
Paul Burke

la réponse ci-dessous est écrite par https://stackoverflow.com/users/3082682/cvizv sur une page qui n'existe plus, puisqu'il n'a pas assez de représentant pour répondre à une question, je la poste . Aucun crédit par moi.

public String getImagePath(Uri uri){
   Cursor cursor = getContentResolver().query(uri, null, null, null, null);
   cursor.moveToFirst();
   String document_id = cursor.getString(0);
   document_id = document_id.substring(document_id.lastIndexOf(":")+1);
   cursor.close();

   cursor = getContentResolver().query( 
   Android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
   null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null);
   cursor.moveToFirst();
   String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
   cursor.close();

   return path;
}

Edit: Il y a un flux sur le code; si le périphérique dispose de plus d'un stockage externe (carte mémoire externe, carte d'usb externe, etc.), le code ci-dessus ne fonctionnera pas.

70
guness

Avant un nouvel accès à la galerie dans KitKat, j’ai eu mon vrai chemin dans sdcard avec cette méthode

Cela n'a jamais été fiable. Il n’est pas nécessaire que le Uri qui vous soit renvoyé d’une demande ACTION_GET_CONTENT ou ACTION_PICK soit indexé par le MediaStore, ou même représente un fichier sur le système de fichiers. La Uri pourrait, par exemple, représenter un flux dans lequel un fichier crypté est déchiffré pour vous à la volée.

Comment pourrais-je réussir à obtenir le vrai chemin dans sdcard?

Il n'y a aucune exigence qu'il y ait un fichier correspondant à la Uri.

Oui, j'ai vraiment besoin d'un chemin

Copiez ensuite le fichier du flux dans votre propre fichier temporaire et utilisez-le. Mieux encore, utilisez le flux directement et évitez le fichier temporaire.

J'ai changé d'intention.ACTION_GET_CONTENT pour Intent.ACTION_PICK

Cela ne va pas aider votre situation. Il n'est pas nécessaire qu'une réponse ACTION_PICK soit pour un Uri qui contient un fichier sur le système de fichiers que vous pouvez dériver de manière magique.

27
CommonsWare

Cette réponse est basée sur votre description quelque peu vague. Je suppose que vous avez déclenché une intention avec action: Intent.ACTION_GET_CONTENT

Et maintenant, vous récupérez content://com.Android.providers.media.documents/document/image:62 au lieu de l'URI du fournisseur de média précédent, n'est-ce pas?

Sur Android 4.4 (KitKat), la nouvelle DocumentsActivity est ouverte lorsqu'un Intent.ACTION_GET_CONTENT est déclenché, ce qui conduit à une vue en grille (ou vue liste) où vous pouvez choisir une image. Les adresses URI suivantes sont renvoyées. contexte d'appel (exemple): content://com.Android.providers.media.documents/document/image:62 (il s'agit des URI du nouveau fournisseur de document, il extrait les données sous-jacentes en fournissant aux clients des URI génériques de fournisseur de document).

Vous pouvez cependant accéder à la fois à la galerie et aux autres activités répondant à Intent.ACTION_GET_CONTENT en utilisant le tiroir dans la zone DocumentsActivity (faites glisser de gauche à droite et vous verrez une interface utilisateur avec galerie dans laquelle choisir de). Comme avant KitKat.

Si vous devez toujours sélectionner la classe DocumentsActivity et que vous avez besoin de l'URI du fichier, vous devriez pouvoir effectuer les opérations suivantes (avertissement: c'est hacky!): Requête (avec contentresolver): content://com.Android.providers.media.documents/document/image:62 URI et lisez la valeur _display_name à partir du curseur . C'est un nom quelque peu unique (juste le nom de fichier sur les fichiers locaux) et utilisez-le dans une sélection (lors d'une requête) auprès du fournisseur de média pour obtenir la ligne correcte correspondant à cette sélection à partir d'ici, vous pouvez également récupérer l'URI du fichier.

Les moyens recommandés pour accéder au fournisseur de document peuvent être trouvés ici (obtenez un inputstream ou un descripteur de fichier pour lire le fichier/bitmap):

Exemples d'utilisation de documentprovider

9
Magnus

J'ai eu exactement le même problème. J'ai besoin du nom de fichier pour pouvoir le télécharger sur un site Web.

Cela a fonctionné pour moi, si je changeais l'intention en PICK. Cela a été testé dans AVD pour Android 4.4 et dans AVD pour Android 2.1.

Ajouter la permission READ_EXTERNAL_STORAGE:

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

Changer l'intention:

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

Je n'ai pas eu à changer mon code pour obtenir le chemin réel:

// 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
Jan Van Overbeke

Essaye ça:

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

Utilisez les éléments suivants dans onActivityResult:

Uri selectedImageURI = data.getData();
input = c.getContentResolver().openInputStream(selectedImageURI);
BitmapFactory.decodeStream(input , null, opts);
6
rahulritesh

Voici une version mise à jour de réponse de Paul Burke . Dans les versions ci-dessous Android 4.4 (KitKat), nous n'avons pas la classe DocumentsContract .

Pour travailler sur les versions ci-dessous, KitKat crée cette classe:

public class DocumentsContract {
    private static final String DOCUMENT_URIS =
        "com.Android.providers.media.documents " +
        "com.Android.externalstorage.documents " +
        "com.Android.providers.downloads.documents " +
        "com.Android.providers.media.documents";

    private static final String PATH_DOCUMENT = "document";
    private static final String TAG = DocumentsContract.class.getSimpleName();

    public static String getDocumentId(Uri documentUri) {
        final List<String> paths = documentUri.getPathSegments();
        if (paths.size() < 2) {
            throw new IllegalArgumentException("Not a document: " + documentUri);
        }

        if (!PATH_DOCUMENT.equals(paths.get(0))) {
            throw new IllegalArgumentException("Not a document: " + documentUri);
        }
        return paths.get(1);
    }

    public static boolean isDocumentUri(Uri uri) {
        final List<String> paths = uri.getPathSegments();
        Logger.v(TAG, "paths[" + paths + "]");
        if (paths.size() < 2) {
            return false;
        }
        if (!PATH_DOCUMENT.equals(paths.get(0))) {
            return false;
        }
        return DOCUMENT_URIS.contains(uri.getAuthority());
    }
}
5
Danylo Volokh

Nous devons apporter les modifications/corrections suivantes dans notre code de sélecteur de galerie onActivityResult () antérieur pour s'exécuter de manière transparente sur Android 4.4 (KitKat) et sur toutes les versions antérieures.

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
}
3
Sachin Gupta