851 votes

android.os.FileUriExposedException: fichier: ///storage/emulated/0/test.txt exposé au-delà de l'application via Intent.getData ()

L'application plante quand j'essaie d'ouvrir un fichier. Il fonctionne en dessous de Android Nougat, mais sur Android Nougat il se bloque. Il ne se bloque lorsque j'essaie d'ouvrir un fichier de la carte SD, pas de la partition système. Quelques problème de permission?

Exemple de code:

File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line

Journal:

android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposés-delà de l'application par le biais d' L'intention.getData()

Edit:

Lorsque ciblant Android Nougat, file:// Uri ne sont plus autorisés. Nous devrions utiliser content:// Uri à la place. Cependant, mon application a besoin d'ouvrir des fichiers dans les répertoires racine. Des idées?

1521voto

Pkosta Points 9780

Si votre targetSdkVersion >= 24, alors nous devons utiliser FileProvider classe de donner accès au fichier ou un dossier particulier pour les rendre accessibles à d'autres applications. Nous avons créer notre propre classe héritant FileProvider afin de faire en sorte que nos FileProvider n'entre pas en conflit avec FileProviders déclaré lors de l'importation de dépendances comme décrit ici.

Étapes pour remplacer file:// URI avec content:// URI:

  • Ajouter une classe étendant FileProvider

    public class GenericFileProvider extends FileProvider {}
    
  • Ajouter un FileProvider <provider> tag AndroidManifest.xml sous <application> balise. Spécifier une autorité unique pour l' android:authorities d'attribut pour éviter les conflits, importé des dépendances peut spécifier ${applicationId}.provider et d'autres autorités.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name=".GenericFileProvider"
            android:authorities="${applicationId}.my.package.name.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • Puis créer un provider_paths.xml fichier res/xml le dossier. Le dossier peut être nécessaire pour le créer si il n'existe pas. Le contenu du fichier est affiché en dessous. Il est décrit que nous aimerions partager l'accès au Stockage Externe à la racine du dossier (path=".") avec le nom external_files.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • L'étape finale consiste à modifier la ligne de code ci-dessous dans

    Uri photoURI = Uri.fromFile(createImageFile());
    

    pour

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());
    
  • Edit: Si vous utilisez une intention de rendre le système de l'ouverture de votre dossier, vous devrez peut-être ajouter la ligne de code suivante:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

Veuillez vous référer, plein de code et la solution a été expliqué ici.

331voto

hqzxzwb Points 2557

En plus de la solution à l'aide de l' FileProvider, il y a une autre façon de contourner ce problème. Simplement mis

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

en Application.onCreate(). De cette façon, la machine virtuelle ignore le fichier URI de l'exposition.

Méthode

builder.detectFileUriExposure()

permet le fichier de vérifier l'exposition, qui est aussi le comportement par défaut si nous n'avons pas l'installation d'un VmPolicy.

J'ai rencontré un problème que si j'utilise un content:// URI envoyer quelque chose, certaines applications ne peuvent tout simplement pas comprendre. Et la dégradation de l' target SDK version n'est pas autorisé. Dans ce cas, ma solution est utile.

Mise à jour:

Comme mentionné dans le commentaire, StrictMode est l'outil de diagnostic, et n'est pas censé être utilisé pour résoudre ce problème. Lorsque j'ai posté cette réponse il y a un an, de nombreuses applications ne peuvent recevoir un Fichier uri. Ils ont juste crash quand j'ai essayé d'envoyer un FileProvider uri. Ceci est corrigé dans la plupart des applications, de sorte que nous devrions aller avec le FileProvider solution.

194voto

Pankaj Lilan Points 1758

Si targetSdkVersion est supérieur à 24, puis FileProvider est utilisé pour accorder l'accès.

Créer un fichier xml(Chemin d'accès: res\xml) provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>


Ajouter un Fournisseur dans AndroidManifest.xml

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

et remplacer

Uri uri = Uri.fromFile(fileImagePath);

pour

Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);

et vous êtes bon pour aller. Espérons que cela aide.

171voto

Pointer Null Points 14229

Si votre application les objectifs de l'API 24+, et que vous voulez/devez utiliser le fichier:// intentions, vous pouvez utiliser hacky moyen de désactiver le moment de l'exécution:

if(Build.VERSION.SDK_INT>=24){
   try{
      Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
      m.invoke(null);
   }catch(Exception e){
      e.printStackTrace();
   }
}

Méthode StrictMode.disableDeathOnFileUriExposure est caché et documentée:

/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/

Le problème, c'est que mon application n'est pas boiteux, mais plutôt ne veut pas être paralysé par l'utilisation d'un contenu:// intentions qui ne sont pas compris par de nombreuses applications. Par exemple, l'ouverture de fichier mp3 avec le contenu:// offre beaucoup moins d'applications que lors de l'ouverture de même sur le fichier:// scheme. Je ne veux pas payer pour Google de défauts de conception, en limitant mon application la fonctionnalité.

Google veut que les développeurs d'utilisation du contenu du système, mais le système n'est pas prêt pour cela, pendant des années, les applications ont été faites pour utiliser des Fichiers pas de "contenu", les fichiers peuvent être édités et sauvegardés en arrière, tandis que les fichiers servi sur le schéma contenu ne peut pas être (peuvent-ils?).

90voto

CommonsWare Points 402670

Si votre targetSdkVersion est de 24 ou plus, vous ne pouvez pas utiliser file: Uri valeurs en Intents sur Android 7.0+ périphériques.

Vos choix sont:

  1. Déposez votre targetSdkVersion à 23 ou inférieur, ou

  2. Mettre votre contenu sur le stockage interne, puis utiliser FileProvider de la rendre disponible de manière sélective à d'autres applications

Par exemple:

Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));

i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);

(à partir de cet exemple de projet)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X