Android Gallery sur Android 4.4 (KitKat) renvoie différents URI pour L'intention.ACTION GET CONTENT

avant KitKat (ou avant la nouvelle galerie) le Intent.ACTION_GET_CONTENT retourné une URI comme ceci

content://media/externe/images/media/3951.

en utilisant le ContentResolver et quering pour MediaStore.Images.Media.DATA renvoie l'URL du fichier.

Dans KitKat toutefois la Galerie renvoie une URI (via le "Dernier") comme ceci:

contenu: / / com.Android.Fournisseur.Média.documents/document / image: 3951

comment je gère ça?

201
demandé sur Peter Mortensen 2013-11-07 15:33:16

19 réponses

essayez ceci:

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

takepersistable turipermission

108
répondu finder 2013-11-09 09:57:11

cela ne nécessite pas de permissions spéciales, et fonctionne avec le cadre D'accès au stockage, ainsi que le modèle non officiel ContentProvider (chemin de fichier dans le 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 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 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 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 index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(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 à jour de cette méthode ici .

171
répondu Paul Burke 2013-12-19 14:48:32

avait le même problème, essayé la solution ci-dessus, mais bien qu'elle ait fonctionné généralement, pour une raison quelconque, je obtenais le déni de permission Sur le fournisseur de contenu Uri pour certaines images bien que j'ai eu la permission android.permission.MANAGE_DOCUMENTS ajouté correctement.

de toute façon trouvé une autre solution qui est de forcer l'ouverture Galerie d'image au lieu de KITKAT documents voir avec:

// KITKAT

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

et ensuite charger l'image:

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

Modifier

ACTION_OPEN_DOCUMENT peut vous obliger à persister les options de permissions, etc et entraîne généralement des Exceptions de sécurité...

une autre solution consiste à utiliser le ACTION_GET_CONTENT combiné avec le c.getContentResolver().openInputStream(selectedImageURI) qui fonctionnera à la fois sur pre-KK et KK. Kitkat utilisera alors new documents view et cette solution fonctionnera avec toutes les applications telles que Photos, Gallery, File Explorer, Dropbox, Google Drive, etc...) mais n'oubliez pas que lors de l'utilisation de cette solution vous vous devez créer une image dans votre onActivityResult() et la stocker sur une carte SD par exemple. Recréer cette image à partir de l'uri sauvegardé lors du lancement de la prochaine application jetterait une Exception de sécurité sur le résolveur de contenu même si vous ajoutez des options de permission comme décrit dans Google API docs (c'est ce qui s'est passé lorsque j'ai fait quelques tests)

en outre, les directives de L'API de développeur Android suggèrent:

ACTION_OPEN_DOCUMENT is not intended to be a Replace for ACTION_GET_CONTENT. Celui que vous devez utiliser dépend des besoins de votre application:

utilisez ACTION_GET_CONTENT si vous voulez que votre application se contente de lire / importer données. Avec cette approche, l'application permet d'importer une copie des données, telles que un fichier image.

utiliser ACTION_OPEN_DOCUMENT si vous voulez que votre application ait accès durable aux documents détenus par un document Fournisseur. Un exemple serait une application de retouche photo qui permet aux utilisateurs d'éditer image stocké dans un fournisseur de documents.

66
répondu voytez 2015-05-25 10:25:06

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

ce que vous devez vraiment faire est d'ouvrir le InputStream à partir du ContentProvider , puis créer un Bitmap à partir de celui-ci. Et il fonctionne sur 4.4 et versions antérieures, nul besoin de réflexion.

    //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 de grandes images, vous devez les charger avec le inSampleSize approprié: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html . Mais c'est un autre sujet.

38
répondu Michał K 2015-01-14 10:54:25

je crois que les réponses déjà publiées devraient amener les gens à aller dans la bonne direction. Cependant, voici ce que j'ai fait qui avait du sens pour le code d'héritage que je mettais à jour. Le code d'héritage utilisait L'URI de la galerie pour changer puis sauvegarder les images.

avant 4.4 (et Google drive), L'URIs ressemblerait à ceci: content://media/externe/images/media/41

comme indiqué dans la question, ILS plus souvent ressembler à ceci: content: / / com.Android.Fournisseur.Média.documents/document / image: 3951

comme j'avais besoin de la possibilité de sauvegarder des images et de ne pas déranger le code existant, j'ai juste copié L'URI de la galerie dans le dossier de données de l'application. Puis a créé un nouvel URI à partir du fichier image sauvegardé 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 */
 }

Note-copyAndClose () just does fichier I / O pour copier InputStream dans un fichier OutputStream. Le code n'est pas affiché.

31
répondu LEO 2017-08-10 20:40:57

Juste voulu dire que cette réponse est génial et je l'utilise depuis longtemps sans problèmes. Mais il ya quelque temps, je suis tombé sur un problème que DownloadsProvider renvoie URIs au format content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf et donc app est écrasé avec NumberFormatException comme il est impossible de parser ses segments uri aussi longtemps. Mais le segment raw: contient de l'uri direct qui peut être utilisé pour récupérer un fichier référencé. Donc je l'ai corrigé en remplaçant isDownloadsDocument(uri) if contenu du texte suivant:

final String 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.toString(), e);
    return null;
}
}
13
répondu Bringoff 2018-07-31 04:51:50

Question

comment obtenir un chemin de fichier réel à partir D'un URI

Réponse

à ma connaissance, nous n'avons pas besoin d'obtenir le chemin du fichier à partir d'une URI parce que dans la plupart des cas, nous pouvons utiliser directement L'URI pour faire notre travail (comme 1. obtenir bitmap 2. L'envoi d'un fichier sur le serveur, etc.)

1. Envoyer au serveur

nous pouvons envoyer directement 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 String getFileNameFromUri(final Uri uri, final Context context) {

    String 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.getString(nameIndex);
                returnCursor.close();
            }
        }
    }
    return fileName;
}

2. Obtention D'une BitMap à partir D'un URI

si L'URI pointe vers l'image, alors nous obtiendrons bitmap, sinon null:

     /**
 * Utilisé pour créer bitmap pour L'URI donné.
 * 

* 1. Convertissez L'URI en bitmap. * 2. Calculer le ratio (en fonction de la taille bitmap) sur combien nous avons besoin pour sous-échantillonner le bitmap original. * 3. Créer bitmap bitmap en fonction de la ration de L'URI. * 4. Référence - /q/how-to-get-bitmap-from-an-uri-9864/"https://developer.android.com/guide/topics/providers/content-provider-basics.html" rel= " nofollow noreferrer ">https://developer.android.com/guide/topics/providers/content-provider-basics.html

  • https://developer.android.com/reference/android/content/ContentResolver.html
  • https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/InputStreamBody.html
  • 7
    répondu Vasanth 2017-08-10 22:05:08

    cette bibliothèque Android gère les changements de cas dans KitKat(y compris les versions oldere- 2.1+):

    https://github.com/iPaulPro/aFileChooser

    utilisez le String path = FileUtils.getPath(context, uri) pour convertir L'Uri retourné en une chaîne de chemin utilisable sur toute la version D'OS. Pour en savoir plus: https://stackoverflow.com/a/20559175/860488

    6
    répondu Morten Holmgaard 2017-05-23 12:10:54

    j'ai combiné Plusieurs réponses dans une solution de travail qui résulte avec le chemin du 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.toString().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);
                }
                String 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 String getPathDeprecated(Context ctx, Uri uri) {
            if( uri == null ) {
                return null;
            }
            String[] 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.getString(column_index);
            }
            return uri.getPath();
        }
    
        public static String 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 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());
        }
    
    }
    
    6
    répondu Grzegorz Pawełczuk 2015-05-05 10:12:16

    pour ceux qui utilisent toujours le code de @Paul Burke avec Android SDK version 23 et supérieure, si votre projet a rencontré l'erreur disant que vous manquez EXTERNAL_PERMISSION, et vous êtes très sûr que vous avez déjà ajouté la permission d'utilisateur dans votre AndroidManifest.fichier xml. C'est parce que vous pouvez dans API Android 23 ou plus et Google rendre nécessaire de garantir l'autorisation de nouveau pendant que vous faites l'action pour accéder au fichier dans l'exécution.

    cela signifie: Si votre SDK version 23 ou plus, on vous demande la permission de lire et D'écrire pendant que vous sélectionnez le fichier image et que vous voulez connaître L'URI de celui-ci.

    et suivant est mon code, en plus de la solution de Paul Burke. J'Ajoute ce code et mon projet commence à bien fonctionner.

    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static final String[] 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é & 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 verifyStoragePermissions method (en tant que type statique pour que je puisse l'appeler dans une autre activité).

    aussi, cela devrait avoir plus de sens si vous faites un État if en premier pour voir si la version SDK actuelle est supérieure à 23 ou non avant d'appeler la méthode verifyStoragePermissions.

    3
    répondu Anthonyeef 2017-08-10 20:54:26

    C'est ce que je fais:

    Uri selectedImageURI = data.getData();    imageFile = new File(getRealPathFromURI(selectedImageURI)); 
    
    private String 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.getString(idx); 
      }
    }
    

    NOTE: managedQuery() méthode est dépréciée, donc je ne l'utilise pas.

    cette réponse est de m3n0R sur la question android get real path par Uri.getPath () et je ne demande aucun crédit. J'ai juste pensé que les gens qui n'ont pas encore résolu ce problème pourraient utiliser ça.

    2
    répondu Isaac Zais 2017-05-23 11:33:24

    s'il vous plaît essayer d'éviter d'utiliser la méthode takePersistableUriPermission parce qu'il a soulevé l'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 résultat de gérer les données de l'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();
            String[] filePathColumn = {MediaStore.Images.Media.DATA};
            Cursor cursor = getContentResolver().query(selectedImage,filePathColumn, null, null, null);
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            String filePath = cursor.getString(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();
            }
    
    
        }
    
    1
    répondu saranya 2015-01-04 19:26:52

    j'ai essayé plusieurs des réponses ici, et je pense que j'ai une solution qui fonctionne à chaque fois et gère les autorisations.

    il est basé sur la solution intelligente de LEO. Ce message devrait contenir tout le code dont vous avez besoin pour faire ce travail, et il devrait fonctionner sur n'importe quel téléphone et Android version;)

    afin d'avoir la possibilité de choisir un fichier à partir d'une carte SD, vous aurez besoin de ces dans votre manifeste:

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

    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 String FILE_TEMP_NAME = "temp_image"; // Whatever file name you like
    

    vérifier la permission et lancer unpick si possible

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

    la Permission de réponse

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

    Gérer la permission de réponse

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

    image de Lancement de sélection

    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 l'Image pick réponse

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

    that's all folks; cela fonctionne pour moi sur tous les téléphones que j'ai.

    1
    répondu Quentin G. 2017-08-10 22:11:42

    c'est un piratage total, mais voilà ce que j'ai fait...

    donc , en jouant avec la configuration d'un DocumentsProvider , j'ai remarqué que le exemple de code (dans getDocIdForFile , autour de la ligne 450) génère un id unique pour un document sélectionné basé sur le chemin (unique) du fichier par rapport à la racine spécifiée que vous lui donnez (c'est-à-dire, ce que vous mettez mBaseDir à la ligne 96).

    pour que L'URI finisse par chercher quelque chose comme:

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

    comme disent les docs, il ne suppose qu'une seule racine (dans mon cas c'est Environment.getExternalStorageDirectory() mais vous pouvez utiliser ailleurs... ensuite, il prend le chemin du fichier, en commençant par la racine, et en fait l'ID unique, en préprogrammant " root: ". Donc je peux déterminer le chemin en éliminant la partie "/document/root: " de l'uri.getPath (), créant un chemin de fichier réel en faisant quelque chose comme ceci:

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // check resultcodes and such, then...
    uri = data.getData();
    if (uri.getAuthority().equals("com.example.provider"))  {
        String path = Environment.getExternalStorageDirectory(0.toString()
                     .concat("/")
                     .concat(uri.getPath().substring("/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.
    }
    

    I savoir. C'est honteux, mais cela a fonctionné. Encore une fois, cela dépend de vous en utilisant votre propre fournisseur de documents dans votre application pour générer le document ID.

    (aussi, il y a une meilleure façon de construire le chemin qui ne suppose pas que " / " est le séparateur de chemin, etc. Mais vous obtenez l'idée.)

    0
    répondu fattire 2013-11-26 07:28:57

    si quelqu'un est intéressé, j'ai fait une version de travail Kotlin pour ACTION_GET_CONTENT :

    var path: String = uri.path // uri = any content Uri
    val databaseUri: Uri
    val selection: String?
    val selectionArgs: Array<String>?
    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)
    }
    
    0
    répondu 0101100101 2017-03-08 06:33:12

    cela a bien fonctionné pour moi:

    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.toString());
    
            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.toString());
        }
    }
    
    0
    répondu Rafa 2017-08-10 20:45:51

    en s'appuyant sur Paul Burke réponse j'ai fait face à beaucoup de problèmes la résolution de carte SD externe du chemin de l'URI comme la plupart des "built-in" retour des fonctions de chemins qui ne sont pas résolus pour les fichiers.

    cependant, c'est mon approche de son // TODO poignée de non-primaire volumes .

    String resolvedPath = "";
    File[] possibleExtSdComposites = context.getExternalFilesDirs(null);
    for (File f : possibleExtSdComposites) {
        // Reset final path
        resolvedPath = "";
    
        // Construct list of folders
        ArrayList<String> extSdSplit = new ArrayList<>(Arrays.asList(f.getPath().split("/")));
    
        // Look for folder "<your_application_id>"
        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/) - e.g. /storage/0000-0000/Android/data/<your_application_id>/files
        ArrayList<String> 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;
    }
    

    noter que cela dépend de la hiérarchie qui pourrait être différente sur chaque fabricant de téléphone - je n'ai pas testé tous (cela a bien fonctionné jusqu'à présent sur Xperia Z3 API 23 et Samsung Galaxy A3 API 23).

    Veuillez confirmer si elle ne donne pas de bons résultats ailleurs.

    0
    répondu Evusas 2017-08-10 22:13:00

    la réponse de @paul burke fonctionne très bien pour les photos caméra et galerie pour API Niveau 19 et au-dessus, mais cela ne fonctionne pas si le SDK minimum de votre projet Android est réglé à moins de 19, et certaines réponses se référant ci-dessus ne fonctionne pas pour la galerie et l'appareil photo. J'ai modifié le code de @paul burke qui fonctionne pour un niveau D'API inférieur à 19. Ci-dessous est le code.

    public static String getPath(final Context context, final Uri uri) {
    
        final boolean isKitKat = Build.VERSION.SDK_INT >=
                                 Build.VERSION_CODES.KITKAT;
        Log.i("URI",uri+"");
        String result = uri+"";
    
        // DocumentProvider
        // if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        if (isKitKat && (result.contains("media.documents"))) {
    
            String[] ary = result.split("/");
            int length = ary.length;
            String imgary = ary[length-1];
            final String[] dat = imgary.split("%3A");
    
            final String docId = dat[1];
            final String 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 String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                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 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;
    }
    
    -1
    répondu parvez rafi 2017-08-10 22:00:33

    La réponse à votre question est que vous devez avoir les autorisations. Tapez le code suivant dans votre manifeste.fichier xml:

    <uses-sdk  android:minSdkVersion="8"   android:targetSdkVersion="18" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_OWNER_DATA"></uses-permission>
    <uses-permission android:name="android.permission.READ_OWNER_DATA"></uses-permission>`
    

    ça a marché pour moi...

    -3
    répondu ashwin 2017-08-10 20:42:15