web-dev-qa-db-fra.com

Android ouvrir un fichier avec les résultats de ACTION_GET_CONTENT dans différents Uri

J'essaie d'ouvrir des fichiers en utilisant Intent.ACTION_GET_CONTENT. Android file browser

Dépendant de la marque Android version/périphérique, le navigateur de fichiers s'ouvre et j'obtiens les résultats suivants:

Sélection d'un fichier à partir de Downloads:

content://com.Android.providers.downloads.documents/document/446

Sélection d'un fichier à partir de Fotos:

content://media/external/images/media/309

Sélection d'un fichier à partir de FileCommander:

file:///storage/emulated/0/DCIM/Camera/20141027_132114.jpg

Je peux ouvrir tous ces fichiers sauf lorsque j'essaie d'ouvrir un fichier à partir de Downloads,, Audio, Afbeeldingen (Images)

Il est probable que je ne puisse pas gérer ce genre d'Uri: content://com.Android.providers.downloads.documents/document/446

J'ai essayé les choses suivantes:

  • Essayer d'ouvrir le fichier avec un nouveau fichier (uri.getPath ()). File.exists () renvoie false.
  • Essayer d’ouvrir/d’atteindre le fichier avec getContext (). GetContentResolver (). OpenInputStream (uri). Résultats dans une exception FileNotFoundException
  • Essayer d'ouvrir le fichier avec le code suivant:

    public static String getRealPathFromURI_API19(Context context, Uri uri) {
    
    Log.i("uri", uri.getPath());
    String filePath = "";
    if (uri.getScheme().equals("file")) {
        return uri.getPath();
    } else if (DocumentsContract.isDocumentUri(context, uri)) {
        String wholeID = DocumentsContract.getDocumentId(uri);
        Log.i("wholeID", wholeID);
        // Split at colon, use second item in the array
        String[] splits = wholeID.split(":");
    if (splits.length == 2) {
        String id = splits[1];
    
            String[] column = {MediaStore.Images.Media.DATA};
        // where id is equal to
            String sel = MediaStore.Images.Media._ID + "=?";
            Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    column, sel, new String[]{id}, null);
            int columnIndex = cursor.getColumnIndex(column[0]);
            if (cursor.moveToFirst()) {
                filePath = cursor.getString(columnIndex);
            }
            cursor.close();
        }
    } else {
        filePath = AttachmentUtils.getPath(context, uri);
    }
    return filePath;
    }
    

Qu'est-ce que je fais mal?

UPDATE: J'ai remarqué que les fichiers répertoriés dans la capture d'écran n'existent pas physiquement dans le stockage. L'appareil que j'utilise est une tablette de la société contenant des données relatives aux ordures. Mon collègue m'a dit que cet appareil était jadis connecté à un autre compte Google. Ces fichiers peuvent être les fichiers du compte précédent qui n'existent plus/sont joignables.

Ma propre conclusion à ce sujet est que je rencontre un bug dans Android. Mon rapport de bug

Mise à jour du 6 février 2017:

Android a interdit le file:// URI. S'il vous plaît envisager de penser à ce sujet.

Interdiction de fichier: schéma Uri Le problème de compatibilité le plus important rencontré jusqu'ici avec Android 7.0) est que le fichier: schéma pour les valeurs Uri est effectivement interdit. Si vous essayez de transmettre un fichier: Uri dans un fichier L’intention qui va à une autre application - que ce soit via une facette supplémentaire ou en tant que "données" de l’intention - vous allez planter avec une exception FileUriExposedException. provenant d'une édition mise à jour de StrictMode. StrictMode.VmPolicy.Builder a une méthode penaltyDeathOnFileUriExposure () qui déclenche la détection des valeurs file: Uri et des exceptions FileUriExposedException qui en résultent. Il semble que cela soit préconfiguré, comme le fait StrictMode. préconfiguré pour appliquer penaltyDeathOnNetwork () (la source de votre défaillance NetworkOnMainThreadException).

32
com2ghz

Utilisez le code ci-dessous. Cela fonctionnera à coup sûr.

public static String getPath(Context context, Uri uri) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat) {
        // DocumentProvider
        if (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;
}

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

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

Utilisez le code ci-dessous pour parcourir le fichier dans n’importe quel format.

public void browseClick() {

    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("*/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    //intent.putExtra("browseCoa", itemToBrowse);
    //Intent chooser = Intent.createChooser(intent, "Select a File to Upload");
    try {
        //startActivityForResult(chooser, FILE_SELECT_CODE);
        startActivityForResult(Intent.createChooser(intent, "Select a File to Upload"),FILE_SELECT_CODE);
    } catch (Exception ex) {
        System.out.println("browseClick :"+ex);//Android.content.ActivityNotFoundException ex
    }
}

Ensuite, obtenez ce chemin de fichier dans onActivityResult comme ci-dessous.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == FILE_SELECT_CODE) {
        if (resultCode == RESULT_OK) {
            try {
              Uri uri = data.getData();

                if (filesize >= FILE_SIZE_LIMIT) {
                    Toast.makeText(this,"The selected file is too large. Selet a new file with size less than 2mb",Toast.LENGTH_LONG).show();
                } else {
                    String mimeType = getContentResolver().getType(uri);
                    if (mimeType == null) {
                        String path = getPath(this, uri);
                        if (path == null) {
                            filename = FilenameUtils.getName(uri.toString());
                        } else {
                            File file = new File(path);
                            filename = file.getName();
                        }
                    } else {
                        Uri returnUri = data.getData();
                        Cursor returnCursor = getContentResolver().query(returnUri, null, null, null, null);
                        int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                        int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
                        returnCursor.moveToFirst();
                        filename = returnCursor.getString(nameIndex);
                        String size = Long.toString(returnCursor.getLong(sizeIndex));
                    }
   File fileSave = getExternalFilesDir(null);
    String sourcePath = getExternalFilesDir(null).toString();
    try {
                        copyFileStream(new File(sourcePath + "/" + filename), uri,this);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
  }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
private void copyFileStream(File dest, Uri uri, Context context)
        throws IOException {
    InputStream is = null;
    OutputStream os = null;
    try {
        is = context.getContentResolver().openInputStream(uri);
        os = new FileOutputStream(dest);
        byte[] buffer = new byte[1024];
        int length;

        while ((length = is.read(buffer)) > 0) {
            os.write(buffer, 0, length);

        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        is.close();
        os.close();
    }
}

Après cela, vous pouvez ouvrir ce fichier à partir du stockage externe de votre application où vous avez enregistré le fichier avec l'action appropriée.

48
KJEjava48