La galerie Android sur Android 4.4 (KitKat) renvoie une adresse URI différente pour Intent.ACTION_GET_CONTENT

Avant KitKat (ou avant la nouvelle galerie), Intent.ACTION_GET_CONTENT renvoyé un URI comme celui-ci.

contenu: // media / external / images / media / 3951.

L’utilisation du ContentResolver et du quering pour MediaStore.Images.Media.DATA renvoyé l’URL du fichier.

Dans KitKat, cependant, la galerie retourne un URI (via “Last”) comme ceci:

content: //com.android.providers.media.documents/document/image: 3951

Comment puis-je gérer cela?

Essaye ça:

 if (Build.VERSION.SDK_INT <19){ Intent intent = new Intent(); intent.setType("image/jpeg"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.select_picture)),GALLERY_INTENT_CALLED); } else { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/jpeg"); startActivityForResult(intent, GALLERY_KITKAT_INTENT_CALLED); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != Activity.RESULT_OK) return; if (null == data) return; Uri originalUri = null; if (requestCode == GALLERY_INTENT_CALLED) { originalUri = data.getData(); } else if (requestCode == GALLERY_KITKAT_INTENT_CALLED) { originalUri = data.getData(); final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // Check for the freshest data. getContentResolver().takePersistableUriPermission(originalUri, takeFlags); } loadSomeStreamAsynkTask(originalUri); } 

Probablement besoin

@SuppressLint ("NewApi")

pour

prendrePersistableUriPermission

Cela ne nécessite aucune autorisation spéciale et fonctionne avec le Storage Access Framework, ainsi que le modèle ContentProvider non officiel (chemin d’access au champ _data ).

 /** * 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 Ssortingng 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 Ssortingng docId = DocumentsContract.getDocumentId(uri); final Ssortingng[] split = docId.split(":"); final Ssortingng type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final Ssortingng 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 Ssortingng docId = DocumentsContract.getDocumentId(uri); final Ssortingng[] split = docId.split(":"); final Ssortingng 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 Ssortingng selection = "_id=?"; final Ssortingng[] selectionArgs = new Ssortingng[] { split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); 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 Ssortingng getDataColumn(Context context, Uri uri, Ssortingng selection, Ssortingng[] selectionArgs) { Cursor cursor = null; final Ssortingng column = "_data"; final Ssortingng[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); return cursor.getSsortingng(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()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } 

Voir une version actualisée de cette méthode ici .

Avait le même problème, essayé la solution ci-dessus, mais si cela fonctionnait généralement, pour une raison quelconque, je obtenais le refus de permission sur le fournisseur de contenu Uri pour certaines images bien que je disposais de l’autorisation android.permission.MANAGE_DOCUMENTS correctement.

Quoi qu’il en soit, nous avons trouvé une autre solution qui consiste à forcer l’ouverture de la galerie d’images au lieu de la vue des documents KITKAT avec:

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

puis chargez l’image:

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

MODIFIER

ACTION_OPEN_DOCUMENT peut nécessiter que vous ACTION_OPEN_DOCUMENT drapeaux d’permissions, etc. et entraîne généralement des exceptions de sécurité …

Une autre solution consiste à utiliser ACTION_GET_CONTENT combinaison avec c.getContentResolver().openInputStream(selectedImageURI) qui fonctionnera à la fois sur KK et sur KK. Kitkat utilisera alors la vue des nouveaux documents et cette solution fonctionnera avec toutes les applications telles que Photos, Galerie, Explorateur de fichiers, Dropbox, Google Drive, etc.) mais n’oubliez pas que lorsque vous utilisez cette solution, vous devez créer une image dans onActivityResult() et stockez-le sur la carte SD par exemple. Recréer cette image à partir de l’URI enregistré au prochain lancement de l’application provoquerait une exception de sécurité sur le résolveur de contenu, même lorsque vous ajoutez des indicateurs d’autorisation, comme décrit dans les documents Google API (c’est ce qui s’est passé lors de tests).

De plus, les directives de l’API Android Developer suggèrent:

ACTION_OPEN_DOCUMENT n’est pas destiné à remplacer ACTION_GET_CONTENT. Celui que vous devriez utiliser dépend des besoins de votre application:

Utilisez ACTION_GET_CONTENT si vous souhaitez que votre application lise / importe simplement des données. Avec cette approche, l’application importe une copie des données, telle qu’un fichier image.

Utilisez ACTION_OPEN_DOCUMENT si vous souhaitez que votre application bénéficie d’un access permanent à long terme aux documents appartenant à un fournisseur de documents. Un exemple serait une application de retouche photo qui permet aux utilisateurs de modifier des images stockées dans un fournisseur de documents.

Tout comme Commonsware mentionné, vous ne devriez pas supposer que le stream que vous obtenez via ContentResolver est convertible en fichier.

Ce que vous devez vraiment faire, c’est ouvrir le InputStream partir du ContentProvider , puis créer un Bitmap à partir de celui-ci. Et ça marche aussi sur les versions 4.4 et antérieures, pas besoin de reflection.

  //cxt -> current context InputStream input; Bitmap bmp; try { input = cxt.getContentResolver().openInputStream(fileUri); bmp = BitmapFactory.decodeStream(input); } catch (FileNotFoundException e1) { } 

Bien sûr, si vous manipulez des images volumineuses, vous devez les charger avec la inSampleSize appropriée: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Mais c’est un autre sujet.

Je crois que les réponses déjà affichées devraient amener les gens à aller dans la bonne direction. Cependant, voici ce que j’ai fait qui était logique pour le code hérité que je mettais à jour. Le code hérité utilisait l’URI de la galerie pour modifier et enregistrer les images.

Avant 4.4 (et Google Drive), les URI ressemblaient à ceci: content: // media / external / images / media / 41

Comme indiqué dans la question, ils ressemblent plus souvent à ceci: content: //com.android.providers.media.documents/document/image: 3951

Comme j’avais besoin de la possibilité d’enregistrer des images et de ne pas perturber le code existant, je viens de copier l’URI de la galerie dans le dossier de données de l’application. Ensuite, un nouvel URI a été créé à partir du fichier image enregistré dans le dossier de données.

Voici l’idée:

 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent), CHOOSE_IMAGE_REQUEST); public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); File tempFile = new File(this.getFilesDir().getAbsolutePath(), "temp_image"); //Copy URI contents into temporary file. try { tempFile.createNewFile(); copyAndClose(this.getContentResolver().openInputStream(data.getData()),new FileOutputStream(tempFile)); } catch (IOException e) { //Log Error } //Now fetch the new URI Uri newUri = Uri.fromFile(tempFile); /* Use new URI object just like you used to */ } 

Remarque – copyAndClose () ne fait que classer les E / S pour copier InputStream dans un FileOutputStream. Le code n’est pas posté.

Je voulais juste dire que cette réponse est géniale et que je l’utilise depuis longtemps sans problème. Mais il ya quelque temps, je suis tombé sur un problème que DownloadsProvider renvoie des URI au format content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf et donc app est NumberFormatException avec NumberFormatException car il est impossible d’parsingr ses segments d’URI aussi longtemps. Mais raw: segment contient des URI directes qui peuvent être utilisées pour récupérer un fichier référencé. Je l’ai donc corrigé en remplaçant isDownloadsDocument(uri) if contenu avec les éléments suivants:

 final Ssortingng id = DocumentsContract.getDocumentId(uri); if (!TextUtils.isEmpty(id)) { if (id.startsWith("raw:")) { return id.replaceFirst("raw:", ""); } try { final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } catch (NumberFormatException e) { Log.e("FileUtils", "Downloads provider returned unexpected uri " + uri.toSsortingng(), e); return null; } } 

Question

Comment obtenir un chemin de fichier réel depuis un URI

Répondre

A ma connaissance, nous n’avons pas besoin d’obtenir le chemin du fichier depuis un URI car pour la plupart des cas, nous pouvons directement utiliser l’URI pour faire notre travail (comme 1. obtenir des bitmap 2. Envoyer un fichier au serveur, etc. .)

1. Envoi au serveur

Nous pouvons directement envoyer le fichier au serveur en utilisant uniquement l’URI.

En utilisant l’URI, nous pouvons obtenir InputStream, que nous pouvons envoyer directement au serveur en utilisant MultiPartEntity.

Exemple

 /** * Used to form Multi Entity for a URI (URI pointing to some file, which we got from other application). * * @param uri URI. * @param context Context. * @return Multi Part Entity. */ public MultipartEntity formMultiPartEntityForUri(final Uri uri, final Context context) { MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Charset.forName("UTF-8")); try { InputStream inputStream = mContext.getContentResolver().openInputStream(uri); if (inputStream != null) { ContentBody contentBody = new InputStreamBody(inputStream, getFileNameFromUri(uri, context)); multipartEntity.addPart("[YOUR_KEY]", contentBody); } } catch (Exception exp) { Log.e("TAG", exp.getMessage()); } return multipartEntity; } /** * Used to get a file name from a URI. * * @param uri URI. * @param context Context. * @return File name from URI. */ public Ssortingng getFileNameFromUri(final Uri uri, final Context context) { Ssortingng fileName = null; if (uri != null) { // Get file name. // File Scheme. if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { File file = new File(uri.getPath()); fileName = file.getName(); } // 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); fileName = returnCursor.getSsortingng(nameIndex); returnCursor.close(); } } } return fileName; } 

2. Obtenir un BitMap d’un URI

Si l’URI pointe sur l’image, nous obtiendrons bitmap, sinon null:

 /** * Used to create bitmap for the given URI. * 

* 1. Convert the given URI to bitmap. * 2. Calculate ratio (depending on bitmap size) on how much we need to subSample the original bitmap. * 3. Create bitmap bitmap depending on the ration from URI. * 4. Reference - http://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri * * @param context Context. * @param uri URI to the file. * @param bitmapSize Bitmap size required in PX. * @return Bitmap bitmap created for the given URI. * @throws IOException */ public static Bitmap createBitmapFromUri(final Context context, Uri uri, final int bitmapSize) throws IOException { // 1. Convert the given URI to bitmap. InputStream input = context.getContentResolver().openInputStream(uri); BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); onlyBoundsOptions.inJustDecodeBounds = true; onlyBoundsOptions.inDither = true;//optional onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional BitmapFactory.decodeStream(input, null, onlyBoundsOptions); input.close(); if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { return null; } // 2. Calculate ratio. int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth; double ratio = (originalSize > bitmapSize) ? (originalSize / bitmapSize) : 1.0; // 3. Create bitmap. BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); bitmapOptions.inDither = true;//optional bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optional input = context.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); input.close(); return bitmap; } /** * For Bitmap option inSampleSize - We need to give value in power of two. * * @param ratio Ratio to be rounded of to power of two. * @return Ratio rounded of to nearest power of two. */ private static int getPowerOfTwoForSampleRatio(final double ratio) { int k = Integer.highestOneBit((int) Math.floor(ratio)); if (k == 0) return 1; else return k; }

commentaires

  1. Android ne fournit aucune méthode pour obtenir un chemin de fichier depuis un URI, et dans la plupart des réponses ci-dessus, nous avons codé certaines constantes, qui peuvent interrompre la publication de la fonctionnalité (désolé, je peux me tromper).
  2. Avant d’aller directement à une solution du chemin d’access au fichier depuis un URI, essayez de résoudre votre cas d’utilisation avec une méthode URI et une méthode par défaut Android.

Référence

  1. https://developer.android.com/guide/topics/providers/content-provider-basics.html
  2. https://developer.android.com/reference/android/content/ContentResolver.html
  3. https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html

Cette bibliothèque Android gère les changements de cas dans KitKat (y compris les anciennes versions – 2.1+):
https://github.com/iPaulPro/aFileChooser

Utilisez le Ssortingng path = FileUtils.getPath(context, uri) pour convertir le Uri renvoyé en une chaîne de chemin utilisable sur toutes les versions du système d’exploitation. Voir plus à ce sujet ici: https://stackoverflow.com/a/20559175/860488

J’ai combiné plusieurs réponses en une seule solution de travail avec un chemin de fichier

Le type MIME n’est pas pertinent pour l’exemple.

  Intent intent; if(Build.VERSION.SDK_INT >= 19){ intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); }else{ intent = new Intent(Intent.ACTION_GET_CONTENT); } intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setType("application/octet-stream"); if(isAdded()){ startActivityForResult(intent, RESULT_CODE); } 

Résultat de manipulation

  @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if( requestCode == RESULT_CODE && resultCode == Activity.RESULT_OK) { Uri uri = data.getData(); if (uri != null && !uri.toSsortingng().isEmpty()) { if(Build.VERSION.SDK_INT >= 19){ final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION; //noinspection ResourceType getActivity().getContentResolver() .takePersistableUriPermission(uri, takeFlags); } Ssortingng filePath = FilePickUtils.getSmartFilePath(getActivity(), uri); // do what you need with it... } } } 

FilePickUtils

 import android.annotation.SuppressLint; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; public class FilePickUtils { private static Ssortingng getPathDeprecated(Context ctx, Uri uri) { if( uri == null ) { return null; } Ssortingng[] projection = { MediaStore.Images.Media.DATA }; Cursor cursor = ctx.getContentResolver().query(uri, projection, null, null, null); if( cursor != null ){ int column_index = cursor .getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getSsortingng(column_index); } return uri.getPath(); } public static Ssortingng getSmartFilePath(Context ctx, Uri uri){ if (Build.VERSION.SDK_INT < 19) { return getPathDeprecated(ctx, uri); } return FilePickUtils.getPath(ctx, uri); } @SuppressLint("NewApi") 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 Ssortingng docId = DocumentsContract.getDocumentId(uri); final Ssortingng[] split = docId.split(":"); final Ssortingng type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final Ssortingng 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 Ssortingng docId = DocumentsContract.getDocumentId(uri); final Ssortingng[] split = docId.split(":"); final Ssortingng 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 Ssortingng selection = "_id=?"; final Ssortingng[] selectionArgs = new Ssortingng[] { 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 Ssortingng getDataColumn(Context context, Uri uri, Ssortingng selection, Ssortingng[] selectionArgs) { Cursor cursor = null; final Ssortingng column = "_data"; final Ssortingng[] 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.getSsortingng(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()); } } 

Pour ceux qui utilisent encore le code de @Paul Burke avec Android SDK version 23 et supérieure, si votre projet a rencontré l’erreur en indiquant qu’il manque EXTERNAL_PERMISSION et que vous êtes certain d’avoir déjà ajouté des droits utilisateur dans votre fichier AndroidManifest.xml. En effet, vous pouvez, dans l’API Android 23 ou supérieure, demander à Google de renouveler votre autorisation pendant que vous effectuez l’action pour accéder au fichier en cours d’exécution.

Cela signifie que: Si votre version du SDK est la version 23 ou supérieure, vous êtes invité à obtenir l’autorisation READ & WRITE pendant que vous sélectionnez le fichier image et souhaitez en connaître l’URI.

Et voici mon code, en plus de la solution de Paul Burke. J’ajoute ces codes et mon projet commence à bien fonctionner.

 private static final int REQUEST_EXTERNAL_STORAGE = 1; private static final Ssortingng[] PERMISSINOS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; public static void verifyStoragePermissions(Activity activity) { int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( activity, PERMISSINOS_STORAGE, REQUEST_EXTERNAL_STORAGE ); } } 

Et dans votre activité et fragment où vous demandez l’URI:

 private void pickPhotoFromGallery() { CompatUtils.verifyStoragePermissions(this); Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); // startActivityForResult(intent, REQUEST_PHOTO_LIBRARY); startActivityForResult(Intent.createChooser(intent, "选择照片"), REQUEST_PHOTO_LIBRARY); } 

Dans mon cas, CompatUtils.java est l’endroit où je définis la méthode verifyStoragePermissions (en tant que type statique pour pouvoir l’appeler dans une autre activité).

De plus, cela devrait être plus logique si vous faites d’abord un état if pour voir si la version actuelle du SDK est supérieure à 23 ou non avant d’appeler la méthode verifyStoragePermissions.

C’est ce que je fais:

 Uri selectedImageURI = data.getData(); imageFile = new File(getRealPathFromURI(selectedImageURI)); private Ssortingng getRealPathFromURI(Uri contentURI) { Cursor cursor = getContentResolver().query(contentURI, null, null, null, null); if (cursor == null) { // Source is Dropbox or other similar local file path return contentURI.getPath(); } else { cursor.moveToFirst(); int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); return cursor.getSsortingng(idx); } } 

REMARQUE: la méthode managedQuery() est obsolète, donc je ne l’utilise pas.

Cette réponse est de m3n0R sur la question android obtenir un chemin réel par Uri.getPath () et je ne réclame aucun crédit. Je pensais simplement que les personnes qui n’avaient pas encore résolu ce problème pouvaient l’utiliser.

Essayez d’éviter d’utiliser la méthode takePersistableUriPermission car elle a déclenché une exception d’exécution pour moi. / ** * Sélectionner dans la galerie. * /

 public void selectFromGallery() { if (Build.VERSION.SDK_INT < AppConstants.KITKAT_API_VERSION) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); ((Activity)mCalledContext).startActivityForResult(intent,AppConstants.GALLERY_INTENT_CALLED); } else { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); ((Activity)mCalledContext).startActivityForResult(intent, AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED); } } 

OnActivity pour le résultat pour gérer les données d'image:

@Override protected void onActivityResult (int requestCode, int resultCode, Intent data) {

  //gallery intent result handling before kit-kat version if(requestCode==AppConstants.GALLERY_INTENT_CALLED && resultCode == RESULT_OK) { Uri selectedImage = data.getData(); Ssortingng[] filePathColumn = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null); cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); Ssortingng filePath = cursor.getSsortingng(columnIndex); cursor.close(); photoFile = new File(filePath); mImgCropping.startCropImage(photoFile,AppConstants.REQUEST_IMAGE_CROP); } //gallery intent result handling after kit-kat version else if (requestCode == AppConstants.GALLERY_AFTER_KITKAT_INTENT_CALLED && resultCode == RESULT_OK) { Uri selectedImage = data.getData(); InputStream input = null; OutputStream output = null; try { //converting the input stream into file to crop the //selected image from sd-card. input = getApplicationContext().getContentResolver().openInputStream(selectedImage); try { photoFile = mImgCropping.createImageFile(); } catch (IOException e) { e.printStackTrace(); }catch(Exception e) { e.printStackTrace(); } output = new FileOutputStream(photoFile); int read = 0; byte[] bytes = new byte[1024]; while ((read = input.read(bytes)) != -1) { try { output.write(bytes, 0, read); } catch (IOException e) { e.printStackTrace(); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } 

J’ai essayé plusieurs des réponses ici, et je pense avoir une solution qui fonctionne à chaque fois et gère également les permissions.

Il est basé sur la solution intelligente de LEO. Cet article devrait contenir tout le code nécessaire pour que cela fonctionne, et il devrait fonctionner sur tous les téléphones et toutes les versions d’Android;)

Pour pouvoir choisir un fichier sur une carte SD, vous en aurez besoin dans votre manifeste:

  

Constantes:

 private static final int PICK_IMAGE = 456; // Whatever number you like public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL = 28528; // Whatever number you like public static final Ssortingng FILE_TEMP_NAME = "temp_image"; // Whatever file name you like 

Vérifiez si possible l’autorisation et lanceImagePick

 if (ContextCompat.checkSelfPermission(getThis(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getThis(), new Ssortingng[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL); } else { launchImagePick(); } 

Réponse d’autorisation

 @Override public void onRequestPermissionsResult(int requestCode, @NonNull Ssortingng permissions[], @NonNull int[] grantResults) { if (manageReadExternalPermissionResponse(this, requestCode, grantResults)) { launchImagePick(); } } 

Gérer la réponse d’autorisation

 public static boolean manageReadExternalPermissionResponse(final Activity activity, int requestCode, int[] grantResults) { if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL) { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission was granted, yay! Do the // contacts-related task you need to do. return true; } else if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) { boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_EXTERNAL_STORAGE); if (!showRationale) { // The user also CHECKED "never ask again". // You can either enable some fall back, // disable features of your app // or open another dialog explaining // again the permission and directing to // the app setting. } else { // The user did NOT check "never ask again". // This is a good place to explain the user // why you need the permission and ask if he/she wants // to accept it (the rationale). } } else { // Permission denied, boo! Disable the // functionality that depends on this permission. } } return false; } 

Lancer la sélection d’image

 private void launchImagePick() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent, PICK_IMAGE); // see onActivityResult } 

Gérer la réponse de sélection d’image

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE) { if (resultCode == Activity.RESULT_OK) { if (data != null && data.getData() != null) { try { InputStream inputStream = getContentResolver().openInputStream(data.getData()) if (inputStream != null) { // No special persmission needed to store the file like that FileOutputStream fos = openFileOutput(FILE_TEMP_NAME, Context.MODE_PRIVATE); final int BUFFER_SIZE = 1 << 10 << 3; // 8 KiB buffer byte[] buffer = new byte[BUFFER_SIZE]; int bytesRead = -1; while ((bytesRead = inputStream.read(buffer)) > -1) { fos.write(buffer, 0, bytesRead); } inputStream.close(); fos.close(); File tempImageFile = new File(getFilesDir()+"/"+FILE_TEMP_NAME); // Do whatever you want with the File // Delete when not needed anymore deleteFile(FILE_TEMP_NAME); } } catch (Exception e) { e.printStackTrace(); } } else { // Error display } } else { // The user did not select any image } } } 

C’est tous les gars Cela fonctionne pour moi sur tous les téléphones que j’ai.

C’est un hack total, mais voici ce que j’ai fait …

Donc, tout en jouant avec la configuration d’un DocumentProvider , j’ai remarqué que l’ exemple de code (dans getDocIdForFile , autour de la ligne 450) génère un identifiant unique pour un document sélectionné en fonction du chemin (unique) du fichier par rapport à la racine spécifiée ( , ce que vous définissez mBaseDir sur la ligne 96).

Donc, l’URI finit par ressembler à quelque chose comme:

content://com.example.provider/document/root:path/to/the/file

As the docs say, it’s assuming only a single root (in my case that’s Environment.getExternalStorageDirectory() but you may use somewhere else… then it takes the file path, starting at the root, and makes it the unique ID, prepending ” root: “. So I can determine the path by eliminating the "/document/root: ” part from uri.getPath(), creating an actual file path by doing something like this:

 public void onActivityResult(int requestCode, int resultCode, Intent data) { // check resultcodes and such, then... uri = data.getData(); if (uri.getAuthority().equals("com.example.provider")) { Ssortingng path = Environment.getExternalStorageDirectory(0.toSsortingng() .concat("/") .concat(uri.getPath().subssortingng("/document/root:".length()))); doSomethingWithThePath(path); } else { // another provider (maybe a cloud-based service such as GDrive) // created this uri. So handle it, or don't. You can allow specific // local filesystem providers, filter non-filesystem path results, etc. } 

Je connais. It’s shameful, but it worked. Again, this relies on you using your own documents provider in your app to generate the document ID.

(Also, there’s a better way to build the path that don’t assume “/” is the path separator, etc. But you get the idea.)

If anyone’s interestd, I made a working Kotlin version for ACTION_GET_CONTENT :

 var path: Ssortingng = uri.path // uri = any content Uri val databaseUri: Uri val selection: Ssortingng? val selectionArgs: Array? if (path.contains("/document/image:")) { // files selected from "Documents" databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI selection = "_id=?" selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1]) } else { // files selected from all other sources, especially on Samsung devices databaseUri = uri selection = null selectionArgs = null } try { val projection = arrayOf(MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.DATE_TAKEN) // some example data you can query val cursor = context.contentResolver.query(databaseUri, projection, selection, selectionArgs, null) if (cursor.moveToFirst()) { // do whatever you like with the data } cursor.close() } catch (e: Exception) { Log.e(TAG, e.message, e) } 

This worked fine for me:

 else if(requestCode == GALLERY_ACTIVITY_NEW && resultCode == Activity.RESULT_OK) { Uri uri = data.getData(); Log.i(TAG, "old uri = " + uri); dumpImageMetaData(uri); try { ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r"); FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); Log.i(TAG, "File descriptor " + fileDescriptor.toSsortingng()); final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); options.inSampleSize = BitmapHelper.calculateInSampleSize(options, User.PICTURE_MAX_WIDTH_IN_PIXELS, User.PICTURE_MAX_HEIGHT_IN_PIXELS); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); imageViewPic.setImageBitmap(bitmap); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); // get byte array here byte[] picData = stream.toByteArray(); ParseFile picFile = new ParseFile(picData); user.setProfilePicture(picFile); } catch(FileNotFoundException exc) { Log.i(TAG, "File not found: " + exc.toSsortingng()); } } 

Building up on Paul Burke’s answer I faced many problems resolving external SD card’s URI path as most of the suggested “built-in” functions return paths which do not get resolved to files.

However, this is my approach of his // TODO handle non-primary volumes .

 Ssortingng resolvedPath = ""; File[] possibleExtSdComposites = context.getExternalFilesDirs(null); for (File f : possibleExtSdComposites) { // Reset final path resolvedPath = ""; // Construct list of folders ArrayList extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/"))); // Look for folder "" int idx = extSdSplit.indexOf(BuildConfig.APPLICATION_ID); // ASSUMPTION: Expected to be found at depth 2 (in this case ExtSdCard's root is /storage/0000-0000/) - eg /storage/0000-0000/Android/data//files ArrayList hierarchyList = new ArrayList<>(extSdSplit.subList(0, idx - 2)); // Construct list containing full possible path to the file hierarchyList.add(tail); String possibleFilePath = TextUtils.join("/", hierarchyList); // If file is found --> success if (idx != -1 && new File(possibleFilePath).exists()) { resolvedPath = possibleFilePath; break; } } if (!resolvedPath.equals("")) { return resolvedPath; } else { return null; } 

Note it depends on hierarchy which might be different on every phone manufacturer – I have not tested them all (it worked well so far on Xperia Z3 API 23 and Samsung Galaxy A3 API 23).

Please confirm if it does not perform well elsewhere.

@paul burke’s answer works fine for both camera and gallery pictures for API level 19 and above, but it doesn’t work if your Android project’s minimum SDK is set to below 19, and some answers referring above doesn’t work for both gallery and camera. Well, I have modified @paul burke’s code which works for API level below 19. Below is the code.

 public static Ssortingng getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; Log.i("URI",uri+""); Ssortingng result = uri+""; // DocumentProvider // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { if (isKitKat && (result.contains("media.documents"))) { Ssortingng[] ary = result.split("/"); int length = ary.length; Ssortingng imgary = ary[length-1]; final Ssortingng[] dat = imgary.split("%3A"); final Ssortingng docId = dat[1]; final Ssortingng type = dat[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { } else if ("audio".equals(type)) { } final Ssortingng selection = "_id=?"; final Ssortingng[] selectionArgs = new Ssortingng[] { dat[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } else if ("content".equalsIgnoreCase(uri.getScheme())) { return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } public static Ssortingng getDataColumn(Context context, Uri uri, Ssortingng selection, Ssortingng[] selectionArgs) { Cursor cursor = null; final Ssortingng column = "_data"; final Ssortingng[] 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.getSsortingng(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } 

The answer to your question is that you need to have permissions. Type the following code in your manifest.xml file:

     ` 

Cela a fonctionné pour moi …