android.os.FileUriExposedException: file: / / / storage/emulated/0 / test.txt ujawnione poza app poprzez Intent.getData()
Aplikacja zawiesza się, gdy próbuję otworzyć plik. Działa pod Androidem Nougat, ale na Androidzie Nougat się zawiesza. To tylko zawiesza się, gdy próbuję otworzyć plik z karty SD, a nie z partycji systemowej. Jakiś problem z pozwoleniem?
Przykładowy kod:
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: plik:///storage/emulated/0/test.JPG / txt exposed beyond app through Intencja.getData ()
Edit:
Podczas celowania w Androida Nugat, file://
URI nie są już dozwolone. Zamiast tego powinniśmy użyć content://
Uri. Jednak moja aplikacja musi otwierać pliki w katalogach głównych. Jakieś pomysły?
16 answers
Jeśli twoja targetSdkVersion >= 24
, to musimy użyć klasy FileProvider
, aby dać dostęp do określonego pliku lub folderu, aby uczynić je dostępnymi dla innych aplikacji. Tworzymy własną klasę dziedziczącą FileProvider
, aby upewnić się, że nasz FileProvider nie koliduje z fileproviderami zadeklarowanymi w importowanych zależnościach, jak opisano tutaj .
Kroki do zastąpienia file://
URI przez content://
URI:
-
Dodaj klasę rozszerzającą
FileProvider
public class GenericFileProvider extends FileProvider {}
Dodaj tag FileProvider w AndroidManifest.xml pod tagiem. Określ unikalny atrybut
android:authorities
aby uniknąć konfliktów, zaimportowane zależności mogą określać${applicationId}.provider
i inne powszechnie używane atrybuty.
<?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>
- następnie utwórz plik
provider_paths.xml
w folderzeres/xml
. Folder może być potrzebny do utworzenia, jeśli nie istnieje. Zawartość pliku pokazana jest poniżej. Opisuje, że chcielibyśmy udostępnić dostęp do zewnętrznej pamięci w folderze głównym(path=".")
o nazwie 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>
-
Ostatnim krokiem jest zmiana linii kodu poniżej w
Uri photoURI = Uri.fromFile(createImageFile());
Do
Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());
-
Edit: jeśli używasz intencji, aby system otworzył plik, może być konieczne dodanie następującej linii kodu:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Proszę odnieść się, Pełny kod i rozwiązanie zostało wyjaśnione tutaj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-29 13:04:51
Oprócz rozwiązania za pomocą FileProvider
, istnieje inny sposób obejścia tego problemu. Po prostu
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
W Application.onCreate()
. W ten sposób maszyna wirtualna ignoruje ekspozycję pliku URI
.
Metoda
builder.detectFileUriExposure()
Włącza sprawdzanie ekspozycji pliku, co jest również domyślnym zachowaniem, jeśli nie ustawiamy VmPolicy.
Napotkałem problem, że jeśli używam content://
URI
aby coś wysłać, niektóre aplikacje po prostu nie mogą tego zrozumieć. I obniżanie wersji target SDK
jest niedozwolone. W w tym przypadku moje rozwiązanie jest przydatne.
Update:
Jak wspomniano w komentarzu, StrictMode jest narzędziem diagnostycznym i nie powinien być używany do tego problemu. Kiedy opublikowałem tę odpowiedź rok temu, wiele aplikacji może odbierać tylko Uri plików. Po prostu się rozbijają, gdy próbowałem wysłać do nich Uri Fileprovidera. Jest to naprawione w większości aplikacji teraz, więc powinniśmy przejść z rozwiązaniem FileProvider.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-06 10:56:40
Jeśli Twoja aplikacja obsługuje API 24+, a nadal chcesz / musisz użyć file: / / intents, możesz użyć hacky way, aby wyłączyć sprawdzenie runtime:
if(Build.VERSION.SDK_INT>=24){
try{
Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
m.invoke(null);
}catch(Exception e){
e.printStackTrace();
}
}
Metoda StrictMode.disableDeathOnFileUriExposure
jest ukryta i udokumentowana jako:
/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/
Problem polega na tym, że moja aplikacja nie jest lame, ale raczej nie chce być kaleką za pomocą treści: / / intencje, które nie są rozumiane przez wiele aplikacji tam. Na przykład, otwarcie pliku mp3 z content: / / scheme oferuje znacznie mniej aplikacji niż przy otwieraniu samego pliku: / / scheme. Nie chcę aby zapłacić za błędy projektowe Google poprzez ograniczenie funkcjonalności mojej aplikacji.
Google chce, aby Programiści korzystali z schematu zawartości, ale system nie jest na to przygotowany, przez lata aplikacje były tworzone do korzystania z Plików Nie "zawartości", pliki można edytować i zapisywać z powrotem, podczas gdy pliki serwowane nad schematem zawartości nie mogą być (prawda?).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-24 11:29:25
Jeśli twój targetSdkVersion
jest 24 lub wyższy, nie możesz używać file:
Uri
wartości w Intents
na urządzeniach z Androidem 7.0 + .
Twoje wybory to:
-
Upuść
targetSdkVersion
do 23 lub niżej, lub -
Umieść zawartość w pamięci wewnętrznej, a następnie użyj
FileProvider
Aby udostępnić go selektywnie innym aplikacjom
Na przykład:
Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-05 12:42:59
Jeśli targetSdkVersion
jest wyższa niż 24, Następnie FileProvider jest używany do przyznania dostępu.
Utwórz plik xml(ścieżka: 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>
Dodaj Provider w 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>
I zastąp
Uri uri = Uri.fromFile(fileImagePath);
Do
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);
I możesz iść. Mam nadzieję, że to pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-01 05:13:34
Najpierw musisz dodać dostawcę do AndroidManifest
<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>
Teraz Utwórz plik w folderze zasobów XML (jeśli używasz android studio, możesz nacisnąć Alt + Enter po podświetleniu ścieżek plików i wybrać opcję Utwórz zasób XML)
Następnie w pliku file_paths wpisz
<?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>
Ten przykład dotyczy zewnętrznej ścieżki, którą możesz referować tutaj , aby uzyskać więcej opcji. Pozwoli to na udostępnianie plików znajdujących się w tym folderze i jego podfolderze.
Teraz wszystko, co jest po lewej jest utworzenie intencji w następujący sposób:
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 : dodałem folder główny karty sd w pliku_paths. Przetestowałem ten kod i działa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-12 19:59:46
@palash K odpowiedź jest poprawna i działa na pliki pamięci wewnętrznej, ale w moim przypadku chcę otwierać pliki z pamięci zewnętrznej również moja aplikacja zawiesiła się podczas otwierania pliku z pamięci zewnętrznej, takiego jak sdcard i usb, ale udaje mi się rozwiązać problem modyfikując provider_paths.xml z zaakceptowanej odpowiedzi
Zmień provider_paths.xml Jak poniżej
<?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>
I w klasie java (bez zmian jako zaakceptowana odpowiedź tylko mała edycja)
Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)
To pomoże mi naprawić awaria plików z zewnętrznych magazynów, mam nadzieję, że pomoże to komuś, kto ma ten sam problem co mój :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-20 21:24:34
Używanie fileprovidera jest dobrym rozwiązaniem. Ale możesz użyć tego prostego obejścia:
WARNING: zostanie to naprawione w następnym wydaniu Androida - https://issuetracker.google.com/issues/37122890#comment4
Zastąpić:
startActivity(intent);
By
startActivity(Intent.createChooser(intent, "Your title"));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-14 07:59:59
Użyłem powyższej odpowiedzi Palasha, ale była ona nieco niekompletna, musiałem udzielić takiej zgody
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);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-11 16:05:31
Po prostu wklej poniższy kod w activity onCreate ()
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());
Zignoruje ekspozycję URI
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-16 06:50:25
Nie wiem dlaczego, zrobiłem wszystko dokładnie tak samo jak Pkosta ( https://stackoverflow.com/a/38858040 ) ale ciągle dostaję błąd:
java.lang.SecurityException: Permission Denial: opening provider redacted from ProcessRecord{redacted} (redacted) that is not exported from uid redacted
val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent
w rzeczywistości ustawiał getIntent().addFlags
zamiast operować na moim nowo zadeklarowanym playIntent.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-17 12:59:37
Aby pobrać plik pdf z serwera, dodaj poniższy kod w swojej klasie usług. Mam nadzieję, że to ci pomoże.
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");
}
I tak, nie zapomnij dodać uprawnień i dostawcy w manifeście.
<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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-28 12:23:57
Po prostu wklej poniższy kod w activity onCreate ()
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder (); StrictMode.setVmPolicy(konstruktor.zbuduj());
Zignoruje ekspozycję URI
Happy coding: -)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 09:15:48
Https://stackoverflow.com/a/38858040/395097 Ta odpowiedź jest kompletna.
Ta odpowiedź jest dla - masz już aplikację, która była kierowana poniżej 24, a teraz uaktualniasz się do targetSDKVersion >= 24.
W Androidzie N zmieniany jest tylko uri pliku wystawionego na aplikację innej firmy. (Nie tak jak wcześniej). Więc zmień tylko miejsca, w których udostępniasz ścieżkę aplikacji 3rd party (aparat w moim przypadku)
W naszej aplikacji wysyłaliśmy uri do aplikacji Camera, w tym miejscu spodziewamy się, że aplikacja aparatu będzie przechowywać przechwycony obraz.
- dla Androida N generujemy nową zawartość: / / URL oparty na uri wskazujący na plik.
- generujemy zwykłą ścieżkę opartą na API plików dla tej samej (przy użyciu starszej metody).
Teraz mamy 2 różne uri dla tego samego pliku. #1 jest współdzielony z aplikacją Camera. Jeśli intencja aparatu się powiedzie, możemy uzyskać dostęp do obrazu z #2.
Mam nadzieję, że to pomoże.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-30 07:20:22
W moim przypadku pozbyłem się wyjątku, zastępując {[0] } tylko SetData
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 00:45:19
Xamarin.Android
Uwaga: ścieżka xml / provider_paths.xml (.axml) nie dało się rozwiązać, nawet po stworzeniu folderu xmlpod Resources (może można go umieścić w istniejącym miejscu jak Values, nie próbowałem), więc uciekłem się do tego, co na razie działa. Testy wykazały, że musi być wywołana tylko raz na każde uruchomienie aplikacji (co ma sens, ponieważ zmienia stan operacyjny hosta maszyny wirtualnej).
Uwaga: xmlmusi być pisany wielkimi literami, więc Resources/Xml/provider_paths.xml
Java.Lang.ClassLoader cl = _this.Context.ClassLoader;
Java.Lang.Class strictMode = cl.LoadClass("android.os.StrictMode");
System.IntPtr ptrStrictMode = JNIEnv.FindClass("android/os/StrictMode");
var method = JNIEnv.GetStaticMethodID(ptrStrictMode, "disableDeathOnFileUriExposure", "()V");
JNIEnv.CallStaticVoidMethod(strictMode.Handle, method);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-26 14:48:15