android.os.FileUriExposedException : file : ///storage/emulated/0/test.txt가 Intent.getData ()를 통해 앱을 넘어 노출됨


738

파일을 열려고 할 때 앱이 충돌합니다. Android Nougat 아래에서 작동하지만 Android Nougat에서는 충돌합니다. 시스템 파티션이 아닌 SD 카드에서 파일을 열려고 할 때만 충돌이 발생합니다. 권한 문제가 있습니까?

샘플 코드 :

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

로그:

android.os.FileUriExposedException : file : ///storage/emulated/0/test.txt가 Intent.getData ()를 통해 앱을 넘어 노출됨

편집하다:

Android Nougat을 타겟팅 할 때 file://더 이상 URI를 사용할 수 없습니다. content://대신 URI를 사용해야 합니다. 그러나 내 응용 프로그램은 루트 디렉토리에서 파일을 열어야합니다. 어떤 아이디어?


20
나는 이것이 앱 개발자들에게 인생을 불필요하게 어렵게 만드는 실수라고 생각합니다. 각 앱에 "FileProvider"및 "authority"를 번들로 제공해야하는 것은 Enterprisey 상용구처럼 보입니다. 모든 파일 의도에 플래그를 추가해야하는 것은 어색하고 불필요합니다. "경로"의 우아한 개념을 깨는 것은 불쾌합니다. 그리고 어떤 이점이 있습니까? 앱에 스토리지 액세스 권한을 선택적으로 부여하는 반면 (대부분의 앱에는 특히 sdcard 액세스 권한이 있으며 특히 파일에서 작동하는 앱은 무엇입니까?)
nyanpasu64

2
이 작고 완벽한 코드를 시도 stackoverflow.com/a/52695444/4997704
Binesh 쿠마

답변:


1316

의 경우 클래스 targetSdkVersion >= 24를 사용 FileProvider하여 특정 파일 또는 폴더에 액세스하여 다른 앱에서 액세스 할 수 있도록해야합니다. 여기에FileProvider 설명 된 것처럼 FileProvider가 가져온 종속성에 선언 된 FileProvider와 충돌하지 않도록하기 위해 자체 클래스 상속 을 만듭니다 . .

file://URI를 content://URI 로 바꾸는 단계 :

  • 수업 확장 추가 FileProvider

    public class GenericFileProvider extends FileProvider {}
  • <provider>태그 AndroidManifest.xml아래에 FileProvider 태그를 추가하십시오 <application>. android:authorities충돌을 피하기 위해 속성에 고유 한 권한을 지정하십시오 . 가져온 종속성이 지정 ${applicationId}.provider하고 기타 일반적으로 사용되는 권한이 있습니다.

<?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}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • 그런 다음 폴더에 provider_paths.xml파일을 만듭니다 res/xml. 폴더가 없으면 작성해야 할 수도 있습니다. 파일 내용은 아래와 같습니다. 루트 폴더의 External Storage에 대한 액세스 권한을 external_files(path=".") 이름 으로 공유하고 싶다고 설명합니다 .
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • 마지막 단계는 아래 코드 줄을 변경하는 것입니다.

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

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
  • 편집 : 시스템에서 파일을 열기 위해 의도를 사용하는 경우 다음 코드 줄을 추가해야 할 수 있습니다.

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

여기에 전체 코드 및 솔루션이 설명되어 있습니다.


62
방금 intent.setFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION)를 추가해야했습니다.
alorma

24
모든 Android 버전에서 작동합니까, 아니면 API 24에서만 작동합니까?
안드로이드 개발자


11
@ rockhammer 방금 Android 5.0, 6.0, 7.1 및 8.1로 이것을 테스트했으며 모든 경우에 작동합니다. 따라서 (Build.VERSION.SDK_INT > M)조건은 쓸모가 없습니다.
Sébastien

66
FileProvider기본 동작을 재정의하려는 경우에만 확장해야합니다 android:name="android.support.v4.content.FileProvider". 그렇지 않으면을 사용하십시오 . developer.android.com/reference/android/support/v4/content/…를
JP Ventura

313

를 사용하는 솔루션 외에도이 문제를 해결할 수있는 FileProvider다른 방법이 있습니다. 간단히 말해서

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

에서 Application.onCreate(). 이러한 방식으로 VM은 파일 URI노출을 무시합니다 .

방법

builder.detectFileUriExposure()

파일 노출 검사를 활성화합니다. 이는 VmPolicy를 설정하지 않은 경우의 기본 동작이기도합니다.

a content:// URI를 사용하여 무언가를 보내면 일부 앱이 이해할 수 없다는 문제가 발생 했습니다. target SDK버전 다운 그레이드는 허용되지 않습니다. 이 경우 내 솔루션이 유용합니다.

최신 정보:

주석에서 언급했듯이 StrictMode는 진단 도구이며이 문제에 사용해서는 안됩니다. 1 년 전에이 답변을 게시했을 때 많은 앱이 File uris 만받을 수 있습니다. FileProvider URI를 보내려고 할 때 충돌이 발생합니다. 현재 대부분의 앱에서 수정되었으므로 FileProvider 솔루션을 사용해야합니다.


1
@LaurynasG API 18에서 23까지, 안드로이드는 기본적으로 파일 URI 노출을 확인하지 않습니다. 이 메소드를 호출하면이 점검이 가능합니다. API 24부터 android는 기본적으로이 확인을 수행합니다. 그러나 새로운을 설정하여 비활성화 할 수 있습니다 VmPolicy.
hqzxzwb

이 작업을 수행하는 데 필요한 다른 단계가 있습니까? Android 7.0을 실행하는 Moto G의 약자이므로 작동하지 않습니다.
CKP78

3
그러나이 문제를 어떻게 해결할 수 있습니까? StrictMode는 개발자 모드에서 릴리스 모드가 아닌 활성화되어야하는 진단 도구입니다 ???
Imene Noomene

1
@ImeneNoomene 실제로 우리는 여기서 StrictMode를 비활성화하고 있습니다. 릴리스 모드에서는 StrictMode를 활성화해서는 안되지만 실제로 Android는 디버그 모드 또는 릴리스 모드에 관계없이 기본적으로 일부 StrictMode 옵션을 활성화합니다. 그러나 어떤 식 으로든이 답변은 일부 대상 앱이 콘텐츠 URI를 수신 할 준비가되지 않았을 때 해결 방법으로 만 사용되었습니다. 대부분의 앱이 컨텐츠 URI에 대한 지원을 추가 했으므로 FileProvider 패턴을 사용해야합니다.
hqzxzwb

3
@ImeneNoomene 나는 당신의 분노에 완전히 당신과 함께 있습니다. 맞습니다.이 도구는 진단 도구이거나 적어도 프로젝트에 추가했을 때였습니다. 이것은 매우 실망입니다! StrictMode.enableDefaults();, 개발 빌드에서만 실행하면이 충돌이 발생하지 않습니다. 이제 충돌하는 프로덕션 응용 프로그램이 있지만 개발 중에는 충돌하지 않습니다. 따라서 기본적으로 진단 도구를 사용하면 심각한 문제가 숨겨집니다. 이것을 이해하는 데 도움을 주신 @hqzxzwb에게 감사드립니다.
Jon

174

경우 targetSdkVersion높은보다 24 다음 FileProvider 액세스 권한을 부여하는 데 사용됩니다.

xml 파일 생성 (경로 : 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>


추가 제공자의 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>

androidx 를 사용 하는 경우 FileProvider 경로는 다음과 같아야합니다.

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

교체

Uri uri = Uri.fromFile(fileImagePath);

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

편집 : URI를 포함하는 동안 Intent아래 줄을 추가하십시오.

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

그리고 당신은 갈 수 있습니다. 도움이 되길 바랍니다.


2
@MaksimKniazev 오류를 간단히 설명해 주시겠습니까? 내가 당신을 도울 수 있도록.
Pankaj Lilan

1
@PankajLilan, 나는 당신이 말한 것을 정확하게했습니다. 그러나 다른 응용 프로그램에서 pdf를 열 때마다 비어 있습니다 (정확하게 저장 됨). XML을 편집해야합니까? FLAG_GRANT_READ_URI_PERMISSION도 이미 추가했습니다.
Felipe Castilhos

1
내 실수는 잘못된 의도에 권한을 추가하고있었습니다. 이것이 가장 좋고 가장 간단한 정답입니다. 감사합니다!
Felipe Castilhos

2
예외 java.lang.IllegalArgumentException이 발생합니다. / 포함 된 구성된 루트를 찾을 수 없습니다. / 내 파일 경로는 /storage/emulated/0/GSMManage_DCIM/Documents/Device7298file_74.pdf입니다. 도와 주실 래요?
Jagdish Bhavsar

1
이유를 알고하지만 난 모두 읽기 및 쓰기 권한을 추가했다하지 마십시오 target.addFlags을 (에서 Intent.FLAG_GRANT_READ_URI_PERMISSION) target.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
상자

160

앱이 API 24 이상을 대상으로하고 여전히 file : // 인 텐트를 사용하려는 경우 해키 방법을 사용하여 런타임 확인을 비활성화 할 수 있습니다.

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

방법 StrictMode.disableDeathOnFileUriExposure은 숨겨지고 다음과 같이 문서화됩니다.

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

문제는 내 앱이 엉망이 아니라 많은 앱에서 이해하지 못하는 content : // 의도를 사용하여 무너지고 싶지 않다는 것입니다. 예를 들어 content : // 구성표로 mp3 파일을 열면 file : // 구성표를 통해 동일한 파일을 열 때보 다 훨씬 적은 수의 응용 프로그램이 제공됩니다. 내 앱의 기능을 제한하여 Google의 디자인 결함에 대한 비용을 지불하고 싶지 않습니다.

Google은 개발자가 콘텐츠 구성표를 사용하기를 원하지만 시스템은이를 위해 준비되지 않았습니다. 몇 년 동안 앱은 "콘텐츠"가 아닌 파일을 사용하고 파일은 편집하고 다시 저장할 수 있지만 콘텐츠 구성표를 통해 제공되는 파일은 사용할 수 없습니다 그들?).


3
"콘텐츠 구성표를 통해 제공되는 파일은 할 수 없습니다." -내용에 대한 쓰기 권한이있는 경우 반드시 확인하십시오. 와 ContentResolver모두 있습니다. 덜 해킹하는 방법은 VM 규칙을 직접 구성하고 규칙을 활성화하지 않는 것 입니다. openInputStream()openOutputStream()file Uri
CommonsWare

1
바로 그거죠. 전체 앱을 빌드 한 다음 25 가지의 모든 카메라 방법을 타겟팅 한 후에 알아내는 것은 어려운 일입니다. 이것은 올바른 방법으로 할 시간이 될 때까지 저에게 효과적입니다.
Matt W

5
안드로이드 7에서 작동합니다. 감사합니다
Anton Kizema

4
Huawei Nexus 6P에서 테스트 한 Android 8에서도 작동합니다.
곤잘로 레데 즈마 토레스

4
나는 이것이 프로덕션에서 작동하고 있음을 확인합니다 (500,000 명 이상의 사용자가 있음). 현재 버전 8.1이 가장 높은 버전이며 작동합니다.
Eli

90

targetSdkVersion24 세 이상인 경우 Android 7.0 이상 기기 에서 값을 사용할 수 없습니다file: UriIntents .

당신의 선택은 :

  1. targetSdkVersion23 이하로 떨어 뜨리 거나

  2. 콘텐츠를 내부 저장소에 저장 한 다음 다른 앱에서 선택적으로 사용할FileProvider 수 있도록 사용

예를 들면 다음과 같습니다.

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

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

( 이 샘플 프로젝트에서 )


답변 해주셔서 감사합니다. 이것을 /system파티션의 파일과 함께 사용하면 어떻게됩니까 ? 모든 앱은 루트없이이 파티션에 액세스 할 수 있어야합니다.
Thomas Vos

2
@SuperThomasLab : /system세계가 읽을 수있는 모든 것을 의지하지는 않습니다 . 내 말은, 당신은 여전히이 예외가 발생한다는 것입니다. 나는 그들이 계획을 확인하고 파일이 실제로 읽을 수 있는지 여부를 결정하려고하지 않는다고 생각합니다. 그러나에서 FileProvider봉사하도록 가르 칠 수 없으므로 도움이되지 않습니다 /system. 문제를 극복 하기 위해 내 맞춤형 전략을 만들거나 나만의 전략을 만들StreamProvider있습니다ContentProvider .
CommonsWare

여전히이 문제를 어떻게 해결할지 생각하고 있습니다. Android N 지원으로 업데이트하는 앱은 루트 브라우저입니다. 그러나 이제는 더 이상 루트 디렉토리에서 파일을 열 수 없습니다. 이 "좋은 변화"때문에 ( /data, /system).
Thomas Vos

1
targetSdkVersion을 23으로 떨어 뜨릴 때 가장 중요한 단점은 무엇입니까? thnx
rommex

2
@ rommex : "가장 중요한"것이 무엇인지 모르겠습니다. 예를 들어, 분할 화면 모드 또는 자유형 다중 창 장치 (Chromebooks, Samsung DeX)에서 작업하는 사용자에게는 앱이 다중 창에서 작동하지 않을 수 있다는 메시지가 표시됩니다. 그것이 중요한지 아닌지는 당신에게 달려 있습니다.
CommonsWare

47

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

이제 xml 리소스 폴더에 파일을 만듭니다 (android studio를 사용하는 경우 file_paths를 강조 표시 한 후 Alt + Enter를 누르고 xml 리소스 옵션 만들기를 선택할 수 있음)

다음 file_paths 파일에 다음을 입력하십시오.

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

이 예제는 여기서 참조 할 수있는 외부 경로입니다. 추가 옵션은 를 . 이렇게하면 해당 폴더와 하위 폴더에있는 파일을 공유 할 수 있습니다.

이제 남은 것은 다음과 같이 의도를 만드는 것입니다.

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

편집 : file_paths에 sd 카드의 루트 폴더를 추가했습니다. 이 코드를 테스트했으며 작동합니다.


1
감사합니다. 또한 파일 확장자를 얻는 더 좋은 방법이 있다는 것을 알려 드리고자합니다. String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString()); 또한 FileProvider를 통해 먼저 읽을 답변을 찾고 Android N 이상에서 파일 권한을 다루는 내용을 이해하는 사람이 좋습니다 . 내부 저장소 대 외부 저장소 및 일반 파일 경로 대 캐시 경로에 대한 옵션이 있습니다.
praneetloke

2
다음과 같은 예외가 발생 java.lang.IllegalArgumentException: Failed to find configured root ...했습니다. 작동하는 유일한 방법은 <files-path path="." name="files_root" />대신 xml 파일에 <external-path ...있습니다. 내 파일이 내부 저장소에 저장되었습니다.
steliosf 2016 년

26

@palash k answer는 정확하고 내부 저장소 파일에 대해 작동했지만 내 경우에는 외부 저장소에서 파일을 열고 싶습니다 .sdcard 및 usb와 같은 외부 저장소에서 파일을 열 때 앱이 다운되었지만 수정하여 문제를 해결할 수 있습니다 provider_paths.xml 허용 된 답변의

아래와 같이 provider_paths.xml을 변경하십시오.

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

그리고 자바 클래스에서 (작은 편집으로 허용되는 답변으로 변경 없음)

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

이것은 외부 저장소의 파일 충돌을 해결하는 데 도움이됩니다.


1
어디에서 찾으 <root-path셨나요? 작동합니다. <external-path path="Android/data/${applicationId}/" name="files_root" />외부 저장소에서 열린 파일에는 영향을 미치지 않았습니다.
t0m

나는 다양한 검색 결과에서 이것을 찾았습니다. 다시 확인하고 최대한 빨리 다시 방문하겠습니다
Ramz

또한 언급 한 외부 저장소는 SD 카드 또는 내장 스토리지입니까?
Ramz

부정확하여 죄송합니다. 나는 Android/data/${applicationId}/SDcard를 의미 했다.
t0m

1
이를 의도에 추가해야합니다. intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
s-hunter

26

내 해결책은 Uri.fromFile ()을 사용하는 대신 파일 경로를 문자열로 'Uri.parse'하는 것입니다.

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, doesn't work in Android 10.
}
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setDataAndType(uri, "text/plain");
startActivity(viewFile);

fromFile ()이 파일 포인터를 사용하는 것 같습니다. 메모리 주소가 모든 앱에 노출 될 때 안전하지 않을 수 있다고 생각합니다. 그러나 파일 경로 문자열은 아무도 아프지 않으므로 FileUriExposedException을 발생시키지 않고 작동합니다.

API 레벨 9에서 27까지 테스트되었습니다! 다른 앱에서 편집 할 텍스트 파일을 성공적으로 엽니 다. FileProvider 또는 Android 지원 라이브러리가 전혀 필요하지 않습니다.


이걸 처음 봤으면 좋겠다. 나는 그것이 나를 위해 일한 것을 증명하지는 않았지만 FileProvider보다 훨씬 덜 성가시다.
데일

이것이 실제로 작동하는 이유에 대한 참고 사항 : 문제를 일으키는 것은 File 포인터가 아니지만 'file : //'경로가있는 경우에만 예외가 발생한다는 사실은 fromFile 앞에 자동으로 추가되지만 구문 분석에는 적용되지 않습니다 .
Xmister

3
예외는 아니지만 파일을 관련 앱으로 보낼 수는 없습니다. 그래서 나를 위해 일하지 않았습니다.
Serdar Samancıoğlu

1
다른 앱이 파일 시스템을 통해 외부 저장소에 액세스한다고 가정 할 수 없으므로 Android 10 이상에서는 실패합니다.
CommonsWare

24

아래 코드를 onCreate () 활동에 붙여 넣으십시오.

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

URI 노출을 무시합니다


1
이것은 솔루션 중 하나이지만 표준 솔루션은 아닙니다. 답변을 내리는 사람들은 이것이 해결책으로 작동하는 코드이기 때문에 잘못되었습니다.
saksham

23

아래 코드를 활동에 붙여 넣으십시오 onCreate().

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

URI 노출은 무시합니다.

행복한 코딩 :-)


1
이것의 단점은 무엇입니까?
제임스 F

1
다른 앱이 파일 시스템을 통해 외부 저장소에 액세스한다고 가정 할 수 없으므로 Android 10 이상에서는 실패합니다.
CommonsWare

18

fileProvider를 사용하는 것이 좋습니다. 그러나이 간단한 해결 방법을 사용할 수 있습니다.

경고 : 다음 Android 릴리스에서 수정 될 예정입니다 -https : //issuetracker.google.com/issues/37122890#comment4

바꾸다:

startActivity(intent);

으로

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

7
선택기는 곧 동일한 수표를 포함하도록 Google에서 패치합니다. 이것은 해결책이 아닙니다.
포인터 널

이것은 작동하지만 향후 안드로이드 버전에서는 작동하지 않습니다.
Diljeet

13

위에 주어진 Palash의 대답을 사용했지만 다소 불완전했습니다.

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

11

아래 코드를 onCreate () 활동에 붙여 넣으십시오.

StrictMode.VmPolicy.Builder 빌더 = 새로운 StrictMode.VmPolicy.Builder (); StrictMode.setVmPolicy (builder.build ());

URI 노출을 무시합니다


이렇게하면 엄격 모드 정책이 제거됩니다. 보안 경고를 무시합니다. 좋은 해결책이 아닙니다.
inspire_coding

다른 앱이 파일 시스템을 통해 외부 저장소에 액세스 할 수 있다고 가정 할 수 없으므로 Android 10 이상에서도 실패합니다.
CommonsWare

7

onCreate 에이 두 줄을 추가하십시오.

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

공유 방법

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..."));

다른 앱이 파일 시스템을 통해 외부 저장소에 액세스한다고 가정 할 수 없으므로 Android 10 이상에서는 실패합니다.
CommonsWare

7

여기 내 해결책 :

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

고해상도 / 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>

내 조각에는 다음 코드가 있습니다.

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

당신이 필요한 전부입니다.

또한 만들 필요없습니다

public class GenericFileProvider extends FileProvider {}

Android 5.0, 6.0 및 Android 9.0에서 테스트했으며 성공했습니다.


이 솔루션을 테스트했으며 약간의 변경만으로도 제대로 작동합니다. intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.putExtra (Intent.EXTRA_STREAM, myPhotoFileUri) intent.type = "image / png"startActivity (Intent.createChooser (intent, " ) "를 통해 공유 이미지) 그것은 안드로이드 7 및 8에 핀 작동
inspire_coding

4

server에서 pdf를 다운로드하려면 서비스 클래스에 아래 코드를 추가하십시오. 이것이 당신에게 도움이되기를 바랍니다.

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

그리고 네, 매니페스트에 권한과 공급자를 추가하는 것을 잊지 마십시오.

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

1
무엇 @xml/provider_paths입니까?
adityasnl

1
@Heisenberg는 URL에서 Rahul Upadhyay 게시물을 참조하십시오 : stackoverflow.com/questions/38555301/…
Bhoomika Chauhan

3

나는 왜 Pkosta ( https://stackoverflow.com/a/38858040 ) 와 똑같은 일을 했지만 오류가 계속 발생 하는 이유를 모르겠습니다 .

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실제로 getIntent().addFlags새로 선언 된 playIntent에서 작동하는 대신 실제로 설정 되었습니다.


2

이미지 경로가 내용에 쉽게 들어가도록이 방법을 사용했습니다.

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
As of Android N, in order to work around this issue, you need to use the FileProvider API

아래에 언급 된 것처럼 3 가지 주요 단계가 있습니다.

1 단계 : 매니페스트 항목

<manifest ...>
    <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>
</manifest>

2 단계 : XML 파일 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>

3 단계 : 코드 변경

File file = ...;
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Old Approach
    install.setDataAndType(Uri.fromFile(file), mimeType);
// End Old approach
// New Approach
    Uri apkURI = FileProvider.getUriForFile(
                             context, 
                             context.getApplicationContext()
                             .getPackageName() + ".provider", file);
    install.setDataAndType(apkURI, mimeType);
    install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// End New Approach
    context.startActivity(install);

1

나는 이것이 꽤 오래된 질문이라는 것을 알고 있지만이 답변은 미래 시청자를위한 것입니다. 그래서 비슷한 문제가 발생하여 연구 한 결과이 접근법의 대안을 찾았습니다.

예를 들어 귀하의 의도 : Kotlin의 경로에서 이미지를 보려면

 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)

아래 주요 기능

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

마찬가지로 이미지 대신 pdf와 같은 다른 파일 형식을 사용할 수 있으며 제 경우에는 잘 작동했습니다.


0

나는이 예외가 왜 발생했는지 알아 내려고 거의 하루를 보냈다. 많은 어려움을 겪은 후이 구성은 완벽하게 작동했습니다 ( Kotlin ).

AndroidManifest.xml

<provider
  android:name="androidx.core.content.FileProvider"
  android:authorities="com.lomza.moviesroom.fileprovider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_paths" />
</provider>

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <files-path name="movies_csv_files" path="."/>
</paths>

의도 자체

fun goToFileIntent(context: Context, file: File): Intent {
    val intent = Intent(Intent.ACTION_VIEW)
    val contentUri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
    val mimeType = context.contentResolver.getType(contentUri)
    intent.setDataAndType(contentUri, mimeType)
    intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION

    return intent
}

여기에 전체 프로세스를 설명합니다 .


-1

https://stackoverflow.com/a/38858040/395097 이 답변은 완료되었습니다.

이 답변은 이미 24 미만의 앱을 타겟팅했으며 이제 targetSDKVersion> = 24로 업그레이드하고 있습니다.

Android N에서는 타사 앱에 노출 된 파일 URI 만 변경됩니다. (이전에 사용했던 방식이 아님). 따라서 타사 앱과 경로를 공유하는 장소 만 변경하십시오 (필자의 경우 카메라).

우리의 응용 프로그램에서 우리는 카메라 응용 프로그램에 uri를 보내고 있었고, 그 위치에서 카메라 응용 프로그램이 캡처 된 이미지를 저장할 것으로 기대합니다.

  1. Android N의 경우 파일을 가리키는 새로운 Content : // URI 기반 URL을 생성합니다.
  2. 우리는 (구식 방법을 사용하여) 동일한 파일 API 기반 경로를 생성합니다.

이제 동일한 파일에 대해 2 개의 다른 URI가 있습니다. # 1은 카메라 앱과 공유됩니다. 카메라 의도가 성공하면 # 2에서 이미지에 액세스 할 수 있습니다.

도움이 되었기를 바랍니다.


1
이미 게시 된 답변을 참조하고 있으며 완료 해야하는 경우 답변 plz에 의견을 작성하십시오.
IgniteCoders

1
@IgniteCoders 메시지에서 명확하게 언급했듯이 대답은 관련 사용 사례를 다룹니다.
Aram

-1

Xamarin.Android

참고 : Resources 아래에 xml 폴더를 만든 후에도 xml / provider_paths.xml (.axml) 경로를 확인할 수 없었습니다 ( 과 같은 기존 위치에 넣을 수 는 있지만 시도하지 않았습니다). 이것은 현재 작동합니다. 테스트 결과, 애플리케이션 실행 당 한 번만 호출하면된다 (호스트 VM의 작동 상태를 변경하는 것이 타당 함).

참고 : xml 은 대문자 여야하므로 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);

-1

@Pkosta의 대답은 이것을하는 한 가지 방법입니다.

MediaStore의 파일은 모든 앱에서 액세스 할 수 있으므로을 사용하는 것 외에도 FileProvider파일 MediaStore(특히 이미지 및 비디오 파일의 경우)에 파일을 삽입 할 수도 있습니다.

MediaStore는 주로 비디오, 오디오 및 이미지 MIME 유형을 대상으로하지만 Android 3.0 (API 레벨 11)부터는 비 미디어 유형도 저장할 수 있습니다 (자세한 내용은 MediaStore.Files 참조). scanFile ()을 사용하여 파일을 MediaStore에 삽입 한 후 공유에 적합한 content : // 스타일 Uri가 제공된 onScanCompleted () 콜백으로 전달됩니다. 시스템 MediaStore에 추가 한 후에는 장치의 모든 앱에서 컨텐츠에 액세스 할 수 있습니다.

예를 들어 다음과 같이 비디오 파일을 MediaStore에 삽입 할 수 있습니다.

ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.DATA, videoFilePath);
Uri contentUri = context.getContentResolver().insert(
      MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);

contentUri는 다음과 같이 content://media/external/video/media/183473직접 전달 될 수 있습니다 Intent.putExtra.

intent.setType("video/*");
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);

이것은 나를 위해 작동하고 사용하는 번거 로움을 저장합니다 FileProvider.


-1

간단히 URI 노출을 무시하게하십시오 ... 생성 후 추가하십시오.

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

다른 앱이 파일 시스템을 통해 외부 저장소에 액세스한다고 가정 할 수 없으므로 Android 10 이상에서는 실패합니다.
CommonsWare

생산 앱에서 사용해서는 안됩니다.
Jorgesys

-1

이 솔루션을 사용해보십시오

매니페스트에 이러한 권한을 부여

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

이미지 캡처 의도

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
                    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
                }

ONACTIVITYRESULT에서 캡처 된 이미지 가져 오기

@Override
            protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                super.onActivityResult(requestCode, resultCode, data);
                if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
                    Bundle extras = data.getExtras();
                    Bitmap imageBitmap = (Bitmap) extras.get("data");
                    // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
                    Uri tempUri = getImageUri(getApplicationContext(), imageBitmap);
                    //DO SOMETHING WITH URI
                }
            } 

이미지 URI를 얻는 방법

public Uri getImageUri(Context inContext, Bitmap inImage) {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
        String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
        return Uri.parse(path);
    }

누구나 왜 투표를 거절했는지 말해 줄 수 있습니까? 이것은 100 % 작동 솔루션입니다.
Abdul Basit Rishi

이렇게하면 전체 그림이 아닌 축소판 만 표시됩니다.
16:01에 Build3r

-2

내 경우 SetDataAndType에는 just 로 대체하여 예외를 제거했습니다 SetData.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.