it-swarm.com.de

android.os.FileUriExposedException: file: ///storage/emulated/0/test.txt über App durch Intent.getData () verfügbar gemacht

Die App stürzt ab, wenn ich versuche, eine Datei zu öffnen. Es funktioniert unter Android Nougat, aber auf Android Nougat stürzt es ab. Es stürzt nur ab, wenn ich versuche, eine Datei von der SD-Karte zu öffnen, nicht von der Systempartition. Irgendein Berechtigungsproblem?

Beispielcode:

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

Log:

Android.os.FileUriExposedException: Datei: ///storage/emulated/0/test.txt über App durch Intent.getData () verfügbar gemacht

Bearbeiten:

Beim Targeting von Android Nougat sind file:// URIs nicht mehr zulässig. Wir sollten stattdessen content:// URIs verwenden. Meine App muss jedoch Dateien in Stammverzeichnissen öffnen. Irgendwelche Ideen?

650
Thomas Vos

Wenn Sie targetSdkVersion >= 24 verwenden, müssen Sie die Klasse FileProvider verwenden, um Zugriff auf die bestimmte Datei oder den Ordner zu gewähren und sie für andere Apps verfügbar zu machen. Wir erstellen eine eigene Klasse, die FileProvider erbt, um sicherzustellen, dass unser FileProvider nicht mit FileProvidern in Konflikt steht, die in importierten Abhängigkeiten deklariert sind, wie hier beschrieben .

Schritte zum Ersetzen von file:// URI durch content:// URI:

  • Fügen Sie eine Klasse hinzu, die FileProvider erweitert.

    public class GenericFileProvider extends FileProvider {}
    
  • Fügen Sie in <provider> unter AndroidManifest.xml ein FileProvider <application> -Tag hinzu. Geben Sie eine eindeutige Berechtigung für das Attribut Android:authorities an, um Konflikte zu vermeiden. Importierte Abhängigkeiten können ${applicationId}.provider und andere häufig verwendete Berechtigungen angeben.

<?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}.fileprovider"
            Android:exported="false"
            Android:grantUriPermissions="true">
            <meta-data
                Android:name="Android.support.FILE_PROVIDER_PATHS"
                Android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • Erstellen Sie dann eine provider_paths.xml -Datei im res/xml -Ordner. Ordner müssen möglicherweise erstellt werden, wenn es nicht vorhanden ist. Der Inhalt der Datei wird unten angezeigt. Es wird beschrieben, dass wir den Zugriff auf den externen Speicher im Stammordner (path=".") mit dem Namen external_files freigeben möchten.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <external-path name="external_files" path="."/>
</paths>
  • Der letzte Schritt besteht darin, die folgende Codezeile in zu ändern

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

    zu

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());
    
  • Bearbeiten: Wenn Sie beabsichtigen, dass das System Ihre Datei öffnet, müssen Sie möglicherweise die folgende Codezeile hinzufügen:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

Bitte beachten Sie, der vollständige Code und die Lösung wurden hier erklärt .

1154
Pkosta

Neben der Lösung mit FileProvider gibt es noch eine andere Möglichkeit, dies zu umgehen. Einfach gesagt

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

in Application.onCreate(). Auf diese Weise ignoriert der VM die Belichtung der Datei URI.

Methode

builder.detectFileUriExposure()

aktiviert die Dateibelichtungsprüfung, die auch das Standardverhalten ist, wenn wir keine VmPolicy einrichten.

Ich bin auf ein Problem gestoßen, bei dem einige Apps etwas nicht verstehen, wenn ich einen content://URI zum Senden verwende. Ein Downgrade der target SDK -Version ist nicht zulässig. In diesem Fall ist meine Lösung nützlich.

pdate:

Wie im Kommentar erwähnt, handelt es sich bei StrictMode um ein Diagnosetool, das für dieses Problem nicht verwendet werden soll. Als ich diese Antwort vor einem Jahr gepostet habe, können viele Apps nur File-Uris empfangen. Sie stürzen gerade ab, als ich versucht habe, einen FileProvider-URL an sie zu senden. Dies ist in den meisten Apps jetzt behoben, daher sollten wir uns für die FileProvider-Lösung entscheiden.

285
hqzxzwb

Wenn Ihre App API 24+ als Ziel hat und Sie weiterhin file: // intents verwenden möchten/müssen, können Sie die Laufzeitüberprüfung mithilfe der Hacky-Methode deaktivieren:

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

Methode StrictMode.disableDeathOnFileUriExposure ist versteckt und dokumentiert als:

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

Das Problem ist, dass meine App nicht lahm ist, sondern durch die Verwendung von content: // intents, die von vielen Apps nicht verstanden werden, nicht behindert werden möchte. Wenn Sie beispielsweise eine MP3-Datei mit dem Inhalt: // Schema öffnen, werden viel weniger Apps angeboten als beim Öffnen derselben mit dem Inhalt: // Schema. Ich möchte nicht für die Designfehler von Google bezahlen, indem ich die Funktionalität meiner App einschränke.

Google möchte, dass Entwickler das Inhaltsschema verwenden, aber das System ist nicht darauf vorbereitet. Jahrelang wurden Apps entwickelt, um Dateien zu verwenden, die nicht "Inhalt" sind. Dateien können bearbeitet und zurückgespeichert werden, während Dateien, die über das Inhaltsschema bereitgestellt werden, nicht (können) Sie?).

148
Pointer Null

Wenn targetSdkVersion höher als 24 ist, wird FileProvider verwendet, um den Zugriff zu gewähren.

Erstellen Sie eine XML-Datei (Pfad: 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>


Fügen Sie einen Anbieter in AndroidManifest.xml hinzu

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

Wenn Sie androidx verwenden, sollte der FileProvider-Pfad wie folgt lauten:

 Android:name="androidx.core.content.FileProvider"

und ersetzen

Uri uri = Uri.fromFile(fileImagePath);

zu

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

Bearbeiten: Wenn Sie die URI mit einem Intent einschließen, stellen Sie sicher, dass folgende Zeile hinzugefügt wird:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

und du bist gut zu gehen. Ich hoffe es hilft.

141
Pankaj Lilan

Wenn Ihr targetSdkVersion 24 oder höher ist, Sie können file:Uri Werte nicht in Intents auf Android 7.0+ Geräten verwenden =.

Ihre Wahlmöglichkeiten sind:

  1. Lassen Sie Ihr targetSdkVersion auf 23 oder niedriger fallen, oder

  2. Stellen Sie Ihren Inhalt in den internen Speicher und verwenden Sie FileProvider , um ihn selektiv für andere Apps verfügbar zu machen

Zum Beispiel:

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

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

(von dieses Beispielprojekt )

85
CommonsWare

Zuerst müssen Sie Ihrem AndroidManifest einen Anbieter hinzufügen

  <application
    ...>
    <activity>
    .... 
    </activity>
    <provider
        Android:name="Android.support.v4.content.FileProvider"
        Android:authorities="com.your.package.fileProvider"
        Android:grantUriPermissions="true"
        Android:exported="false">
        <meta-data
            Android:name="Android.support.FILE_PROVIDER_PATHS"
            Android:resource="@xml/file_paths" />
    </provider>
  </application>

erstellen Sie jetzt eine Datei im XML-Ressourcenordner (wenn Sie Android Studio verwenden, können Sie Alt + Eingabetaste drücken, nachdem Sie file_paths markiert und die Option zum Erstellen einer XML-Ressource ausgewählt haben).

Als nächstes in die Datei file_paths eingeben

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path path="Android/data/com.your.package/" name="files_root" />
  <external-path path="." name="external_storage_root" />
</paths>

Dieses Beispiel gilt für den externen Pfad. Weitere Optionen finden Sie unter hier . Auf diese Weise können Sie Dateien freigeben, die sich in diesem Ordner und seinem Unterordner befinden.

Jetzt müssen Sie nur noch die Absicht wie folgt erstellen:

    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1);
    String type = mime.getMimeTypeFromExtension(ext);
    try {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.package.fileProvider", newFile);
            intent.setDataAndType(contentUri, type);
        } else {
            intent.setDataAndType(Uri.fromFile(newFile), type);
        }
        startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT);
    } catch (ActivityNotFoundException anfe) {
        Toast.makeText(getContext(), "No activity found to open this attachment.", Toast.LENGTH_LONG).show();
    }

EDIT: Ich habe den Root-Ordner der SD-Karte in den Dateipfaden hinzugefügt. Ich habe diesen Code getestet und es funktioniert.

46
Karn Patel

@palash k Antwort ist korrekt und funktioniert für interne Speicherdateien, aber in meinem Fall möchte ich auch Dateien von externem Speicher öffnen, meine App stürzte ab, wenn ich Dateien von externem Speicher wie SD-Karte und USB öffne, aber ich schaffe es, das Problem durch Ändern zu lösen provider_paths.xml aus der akzeptierten Antwort

ändern Sie das provider_paths.xml wie unten

<?xml version="1.0" encoding="utf-8"?>
 <paths xmlns:Android="http://schemas.Android.com/apk/res/Android">

<external-path path="Android/data/${applicationId}/" name="files_root" />

<root-path
    name="root"
    path="/" />

</paths>

und in Java Klasse (Keine Änderung als akzeptierte Antwort, nur eine kleine Änderung)

Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)

Dies hilft mir, den Absturz für Dateien aus externen Speichern zu beheben.

25
Ramz

Fügen Sie einfach den folgenden Code in die Aktivität onCreate () ein.

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

URI-Belichtung wird ignoriert

21
Kaushal Sachan

Meine Lösung bestand darin, "Uri.parse" den Dateipfad als Zeichenfolge anstelle von "Uri.fromFile ()" zu verwenden.

String storage = Environment.getExternalStorageDirectory().toString() + "/test.txt";
File file = new File(storage);
Uri uri;
if (Build.VERSION.SDK_INT < 24) {
    uri = Uri.fromFile(file);
} else {
    uri = Uri.parse(file.getPath()); // My work-around for new SDKs, causes ActivityNotFoundException in API 10.
}
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setDataAndType(uri, "text/plain");
startActivity(viewFile);

Scheint, dass fromFile () einen Dateizeiger verwendet, von dem ich annehme, dass er unsicher ist, wenn Speicheradressen für alle Apps verfügbar sind. Ein Dateipfad-String hat jedoch niemanden verletzt, sodass er funktioniert, ohne FileUriExposedException auszulösen.

Getestet auf API Level 9 bis 27! Öffnet die Textdatei erfolgreich zur Bearbeitung in einer anderen App. Benötigt weder FileProvider noch die Android Support-Bibliothek.

19
CrazyJ36

Der fileProvider ist der richtige Weg. Sie können diese einfache Problemumgehung verwenden:

WARNING: Dies wird in der nächsten Android Version - https://issuetracker.google.com) behoben/issues/37122890 # comment4

ersetzen:

startActivity(intent);

durch

startActivity(Intent.createChooser(intent, "Your title"));
18
Simon

Fügen Sie einfach den folgenden Code in die Aktivität onCreate() ein.

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

URI-Belichtung wird ignoriert.

Viel Spaß beim Programmieren :-)

18
Ripdaman Singh

Ich habe die oben gegebene Antwort von Palash verwendet, aber sie war etwas unvollständig, ich musste die Erlaubnis dazu geben

Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", new File(path));

        List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }else {
        uri = Uri.fromFile(new File(path));
    }

    intent.setDataAndType(uri, "application/vnd.Android.package-archive");

    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    startActivity(intent);
12
Max

Fügen Sie einfach den folgenden Code in die Aktivität onCreate () ein.

Builder für StrictMode.VmPolicy.Builder = neu StrictMode.VmPolicy.Builder (); StrictMode.setVmPolicy (builder.build ());

URI-Belichtung wird ignoriert

6
AnilS

füge diese zwei Zeilen in onCreate hinz

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

Share-Methode

File dir = new File(Environment.getExternalStorageDirectory(), "ColorStory");
File imgFile = new File(dir, "0.png");
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.setType("image/*");
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imgFile));
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(sendIntent, "Share images..."));
5
DEVSHK

Fügen Sie zum Herunterladen von PDF-Dateien vom Server den folgenden Code in Ihre Serviceklasse ein. Hoffe das ist hilfreich für dich.

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName + ".pdf");
    intent = new Intent(Intent.ACTION_VIEW);
    //Log.e("pathOpen", file.getPath());

    Uri contentUri;
    contentUri = Uri.fromFile(file);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

    if (Build.VERSION.SDK_INT >= 24) {

        Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
        intent.setDataAndType(apkURI, "application/pdf");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    } else {

        intent.setDataAndType(contentUri, "application/pdf");
    }

Und ja, vergessen Sie nicht, Berechtigungen und Anbieter in Ihrem Manifest hinzuzufügen.

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

<application

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

</application>
4

Ich weiß nicht warum, ich habe alles genauso gemacht wie Pkosta ( https://stackoverflow.com/a/3885804 ), habe aber immer wieder den Fehler erhalten:

Java.lang.SecurityException: Permission Denial: opening provider redacted from ProcessRecord{redacted} (redacted) that is not exported from uid redacted

Ich habe Stunden mit diesem Thema verschwendet. Der Täter? Kotlin.

val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

intent hat tatsächlich getIntent().addFlags gesetzt, anstatt auf meinem neu deklarierten playIntent zu arbeiten.

3
jimbo1qaz

ich setze diese Methode so, dass imageuri Pfad leicht in den Inhalt zu bekommen.

enter code here
public Uri getImageUri(Context context, Bitmap inImage)
{
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    inImage.compress(Bitmap.CompressFormat.PNG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(context.getContentResolver(), 
    inImage, "Title", null);
    return Uri.parse(path);
}
2
Jitu Batiya

Ich weiß, dass dies eine ziemlich alte Frage ist, aber diese Antwort ist für zukünftige Zuschauer bestimmt. Ich bin also auf ein ähnliches Problem gestoßen und habe nach Recherchen eine Alternative zu diesem Ansatz gefunden.

Ihre Absicht hier für zB: Um Ihr Bild von Ihrem Weg in Kotlin zu sehen

 val intent = Intent()
 intent.setAction(Intent.ACTION_VIEW)
 val file = File(currentUri)
 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
 val contentURI = getContentUri(context!!, file.absolutePath)
 intent.setDataAndType(contentURI,"image/*")
 startActivity(intent)

Hauptfunktion unten

private fun getContentUri(context:Context, absPath:String):Uri? {
        val cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            arrayOf<String>(MediaStore.Images.Media._ID),
            MediaStore.Images.Media.DATA + "=? ",
            arrayOf<String>(absPath), null)
        if (cursor != null && cursor.moveToFirst())
        {
            val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID))
            return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Integer.toString(id))
        }
        else if (!absPath.isEmpty())
        {
            val values = ContentValues()
            values.put(MediaStore.Images.Media.DATA, absPath)
            return context.getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        }
        else
        {
            return null
        }
    }

Ebenso können Sie anstelle eines Bildes ein beliebiges anderes Dateiformat wie pdf verwenden, und in meinem Fall hat es einwandfrei funktioniert

1
S.Sho

Hier meine Lösung:

in Manifest.xml

<application
            Android:name=".main.MainApp"
            Android:allowBackup="true"
            Android:icon="@drawable/ic_app"
            Android:label="@string/application_name"
            Android:logo="@drawable/ic_app_logo"
            Android:theme="@style/MainAppBaseTheme">

        <provider
                Android:name="androidx.core.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>

in 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>

in meinem Fragment habe ich den nächsten Code:

 Uri myPhotoFileUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", myPhotoFile);               
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, myPhotoFileUri);

Das ist alles was Sie brauchen.

Auch nicht nötig zu erstellen

public class GenericFileProvider extends FileProvider {}

Ich teste auf Android 5.0, 6.0 und Android 9.0 und es ist eine erfolgreiche Arbeit.

1
Alex