source

안드로이드.os.노출된 파일 URI예외: 파일://storage/emulated/0/test.txt가 앱 이상에서 Intent.getData()를 통해 노출되었습니다.

nicesource 2023. 6. 23. 22:16
반응형

안드로이드.os.노출된 파일 URI예외: 파일://storage/emulated/0/test.txt가 앱 이상에서 Intent.getData()를 통해 노출되었습니다.

파일을 열려고 할 때 앱이 작동하지 않습니다.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

로그:

안드로이드.os.노출된 파일 URI예외: 파일://storage/emulated/0/test.txt가 앱 이상에서 Intent.getData()를 통해 노출되었습니다.

편집:

Nougat을 할 때, Android Nougat을타때할겟로으,,file://URI는 더 이상 허용되지 않습니다.우리는 사용해야 합니다.content://URI. 은 루트 을 열어야 .그러나 내 앱은 루트 디렉터리의 파일을 열어야 합니다.아이디어 있어요?

만약 당신이targetSdkVersion >= 24그러면 우리는 사용해야 합니다.FileProvider클래스 - 특정 파일 또는 폴더에 대한 액세스 권한을 부여하여 다른 앱에서 액세스할 수 있도록 합니다.우리는 다음을 이어받아 우리만의 클래스를 만듭니다.FileProvider파일 공급자가 여기에 설명된 대로 가져온 종속성에 선언된 파일 공급자와 충돌하지 않도록 합니다.

교할단을(를) 대체 file://가 있는 »content://URI:

  • 추가하기<provider>에 꼬리표를 달다.AndroidManifest.xml<application>합니다. 의고권지정니다합을한유태그▁tag의니다지▁for합.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="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>
    </application>
</manifest>
  • 다음 그다음다를 .provider_paths.xml줄을 지어 들어가다res/xml폴더를 누릅니다.폴더가 아직 존재하지 않는 경우 폴더를 만들어야 할 수 있습니다.파일의 내용은 아래와 같습니다.루트 폴더에 있는 외부 저장소에 대한 액세스를 공유하고 싶다고 설명합니다.(path=".")external_files라는 이름을 사용합니다.
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <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);
    

여기에 설명된 전체 코드 및 솔루션을 참조하십시오.

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

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

Application.onCreate() " "VM"을 합니다.URI노출.

방법

builder.detectFileUriExposure()

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

사용할 경우 문제가 발생했습니다.content:// URI어떤 것을 보내기 위해, 어떤 앱들은 그것을 이해하지 못합니다.그리고 그것을 다운그레이드하는 것.target SDK버전이 허용되지 않습니다.이 경우 제 해결책이 유용합니다.

업데이트:

설명에 언급된 것처럼 StrictMode는 진단 도구이므로 이 문제에 사용해서는 안 됩니다.1년 전 이 답변을 올렸을 때 많은 앱이 파일루리스만 받을 수 있습니다.파일 공급자 uri를 보내려고 할 때 파일이 충돌합니다.이 문제는 현재 대부분의 앱에서 해결되었으므로 파일 공급자 솔루션을 사용해야 합니다.

한다면targetSdkVersion24보다 높으면 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);

그리고 당신은 가도 좋습니다.

만약 당신의 앱이 API 24+를 대상으로 하고, 여전히 file://intents를 사용하고 싶다면, hacky 방법을 사용하여 런타임 검사를 비활성화할 수 있습니다.

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

문제는 제 앱이 절름거리는 것이 아니라, 많은 앱이 이해하지 못하는 콘텐츠:// 의도를 사용하여 장애를 일으키고 싶지 않다는 것입니다.예를 들어 mp3 파일을 content://scheme으로 열면 file://scheme을 통해 동일한 파일을 열 때보다 훨씬 적은 앱이 제공됩니다.나는 내 앱의 기능을 제한함으로써 구글의 디자인 결함에 대한 대가를 지불하고 싶지 않습니다.

Google은 개발자들이 콘텐츠 구성표를 사용하기를 원하지만, 시스템은 이에 대한 준비가 되어 있지 않습니다. 수년 동안 앱은 "콘텐츠"가 아닌 파일을 사용하도록 만들어졌고, 파일은 편집 및 저장할 수 있는 반면, 콘텐츠 구성표를 통해 제공되는 파일은 그럴 수 없습니다.

만약 당신이targetSdkVersion24 이상이면 Android 7.0+ 장치에서는 값을 사용Uri없습니다.

선택할 수 있는 항목:

  1. 좀 해요!targetSdkVersion또는 23

  2. 컨텐츠를 내부 스토리지에 저장한 다음 을 사용하여 다른 앱에서 선택적으로 사용할 수 있도록 합니다.

예:

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

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

(이 샘플 프로젝트에서)

먼저 Android Manifest에 공급자를 추가해야 합니다.

  <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를 누르고 create 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 카드의 루트 폴더를 추가했습니다.나는 이 코드를 테스트했고 그것은 작동합니다.

내 해결책은 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 SDKs up to 29.
}
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setDataAndType(uri, "text/plain");
startActivity(viewFile);

fromFile()이 파일 포인터를 사용하는 것 같습니다. 메모리 주소가 모든 앱에 노출되면 안전하지 않을 수 있습니다.그러나 파일 경로 문자열은 아무도 해치지 않으므로 FileUriExposed를 던지지 않고 작동합니다.예외.

API 레벨 9~29에서 테스트 완료!다른 앱에서 편집하기 위해 텍스트 파일을 열었습니다.파일 공급자나 Android 지원 라이브러리가 전혀 필요하지 않습니다.또한 getExternalStorageDirectory()가 더 이상 사용되지 않으므로 API 레벨 30(Android 11) 이상에서는 제대로 작동하지 않습니다.

@palashk 답변이 올바르고 내부 스토리지 파일에 대해 작동했지만, 저의 경우에도 외부 스토리지에서 파일을 열고 싶을 때 sdcard나 usb와 같은 외부 스토리지에서 파일을 열 때 앱이 충돌했지만, 수락된 답변에서 provider_paths.xml을 수정하여 문제를 해결했습니다.

아래와 같이 provider_provider.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>

그리고 java 클래스에서 (승인된 답변으로 변경되지 않고 단지 작은 편집)

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

이것은 외장 스토리지의 파일 충돌을 해결하는 데 도움이 됩니다. 이것이 저와 같은 문제를 가진 누군가에게 도움이 되기를 바랍니다:)

를 활동에 붙여넣으면 됩니다.onCreate().

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

URI 노출을 무시합니다.

해피 코딩 :-)

를 Activity 를▁activity▁the▁in에 붙여넣기만 하면 .onCreate():

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

URI 노출을 무시합니다.

파일 공급자를 사용하는 것이 방법입니다.그러나 다음과 같은 간단한 해결 방법을 사용할 수 있습니다.

경고:다음 안드로이드 릴리즈에서 수정될 예정입니다 - https://issuetracker.google.com/issues/37122890#comment4

대체:

startActivity(intent);

타고

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

위에 나온 팔라쉬의 답변을 사용했는데 다소 불완전해서 이렇게 허락을 해야 했습니다.

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

를 Activity 를▁activity▁the▁in에 붙여넣기만 하면 .onCreate():

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

URI 노출을 무시합니다.

여기 내 해결책이 있습니다.

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>

res/xml/messages_messages.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 {}

저는 안드로이드 5.0, 6.0, 안드로이드 9.0에서 테스트를 해보고 성공작입니다.

작성 시 이 두 줄을 추가합니다.

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

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

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는 실로설고있니다었습하정제다▁actually▁setting▁was를 설정하고 있었습니다.getIntent().addFlags내가 새로 발표한 연극을 수술하는 대신에.

As of Android N, in order to work around this issue, you need to use the FileProvider API

여기에는 아래에 언급된 것과 같이 세 가지 주요 단계가 있습니다.

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

저는 제가 왜 이런 예외를 갖게 되었는지 알아내려고 거의 하루를 보냈습니다.많은 어려움 끝에 이 구성은 완벽하게 작동했습니다(Kotlin).

Android Manifest.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_syslog.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
}

저는 여기서 모든 과정을 설명합니다.

안드로이드 버전 > 24인 경우 나는 방금 팔로우를 완료했습니다.

File fl = new File(url);
    Uri uri = Uri.fromFile(fl);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    if (android.os.Build.VERSION.SDK_INT>=24)
    {
        Context context = getApplicationContext();
        uri = FileProvider.getUriForFile(
                context,
                context.getApplicationContext()
                        .getPackageName() + ".provider", fl);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
    intent.setDataAndType(uri, mimetype);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);

이 링크에 이어 https://medium.com/ @ali.muzaffar/what-is-syslog-os-fileuri가 예외로 노출되었습니다. 그리고 당신이 할 수 있는 것-it-70b9eb17c6d0#.54odzsnk4

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

사하는 것외에를 사용하는 것 에도.FileProvider을 파을에삽수있습다니도할입에 .MediaStore 이미지 및 파일의 ), 수 때문입니다. ("MediaStore") MediaStore의 파일은 다음과 같습니다.

MediaStore는 주로 비디오, 오디오 및 이미지 MIME 유형을 대상으로 하지만 Android 3.0(API 레벨 11)부터는 미디어가 아닌 유형도 저장할 수 있습니다(MediaStore 참조).자세한 내용은 파일)을 참조.scanFile()을 사용하여 MediaStore에 파일을 삽입한 후 공유에 적합한 content://styleUri가 ScanCompleted() 콜백에 제공된 에 전달됩니다.시스템에 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.

나는 이미지우리 경로가 쉽게 콘텐츠에 들어갈 수 있도록 이 방법을 넣었습니다.

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

저는 이것이 꽤 오래된 질문이라는 것을 알지만, 이 대답은 미래의 시청자들을 위한 것입니다.그래서 저는 비슷한 문제에 부딪혔고 연구한 후에 이 접근법의 대안을 찾았습니다.

여기에 대한 귀하의 의도:코틀린의 경로에서 이미지 보기

 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와 같은 다른 파일 형식을 사용할 수 있고, 저의 경우에는 잘 작동했습니다.

앱의 범위가 지정된 스토리지에서 이미지를 공유하고 싶었기 때문에 이 예외가 발생했습니다.몇 시간 동안 검색한 끝에 마침내 이 블로그를 발견했습니다.

내용이 좀 길어서 요점을 말씀드리지만, 검토해보시길 추천해드리겠습니다.

중요한 것은 앱의 범위가 지정된 스토리지에서 어떤 것도 공유할 수 없다는 것입니다.또한 Android 12에서는 의도 선택 하단 대화 상자에 공유 중인 이미지의 미리 보기가 표시되어 매우 멋지지만 범위가 지정된 스토리지 URI에서 미리 보기를 로드할 수 없습니다.

해결책은 캐시 디렉토리에서 공유할 '의도' 파일의 복사본을 만드는 것입니다.

val cachePath = File(externalCacheDir, "my_images/")
cachePath.mkdirs()
val bitmap = loadImageFromStorage(currentQuote.bookId)
val file = File(cachePath, "cache.png")
val fileOutputStream: FileOutputStream
try {
    fileOutputStream = FileOutputStream(file)
    bitmap?.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
    fileOutputStream.flush()
    fileOutputStream.close()
} catch (e: FileNotFoundException) {
    e.printStackTrace()
} catch (e: IOException) {
    e.printStackTrace()
}
val cacheImageUri: Uri = FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", file)
            
val intent = Intent(Intent.ACTION_SEND).apply {
    clipData = ClipData.newRawUri(null, cacheImageUri)
    putExtra(Intent.EXTRA_STREAM, cacheImageUri)
    type = "image/ *"
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(intent, null))
            

범위가 지정된 저장소에서 파일을 로드하는 방법은 다음과 같습니다.

fun Context.loadImageFromStorage(path: String): Bitmap? {
    try {
        val file = getFile(path)
        val bitmap = BitmapFactory.decodeStream(FileInputStream(file))
        return bitmap
    } catch (e: Exception) {
        e.printStackTrace()

        //Returning file from public storage in case the file is stored in public storage 
        return BitmapFactory.decodeStream(FileInputStream(File(path)))
    }
    
    return null
}


fun Context.getFile(path: String): File? {
    val cw = ContextWrapper(this)
    val directory = cw.getDir("image_dir", Context.MODE_PRIVATE)
    if (!directory.exists())
        directory.mkdir()
    try {
        val fileName = directory.absolutePath + "/" + path.split("/").last()
        return File(fileName)
    } catch (e: Exception) {
        e.printStackTrace()
    }
    
    return null
}

마지막으로 업데이트하는 것을 잊지 마십시오.provider_paths.xml파일

<external-cache-path name="external_cache" path="." />

<external-cache-path name="external_files" path="my_images/"/>

사마린.안드로이드

참고: 리소스 아래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);

작동합니다.

 val uri = if (Build.VERSION.SDK_INT < 24) Uri.fromFile(file) else Uri.parse(file.path)
                val shareIntent = Intent().apply {
                    action = Intent.ACTION_SEND
                    type = "application/pdf"
                    putExtra(Intent.EXTRA_STREAM, uri)
                    putExtra(
                        Intent.EXTRA_SUBJECT,
                        "Purchase Bill..."
                    )
                    putExtra(
                        Intent.EXTRA_TEXT,
                        "Sharing Bill purchase items..."
                    )
                }
                startActivity(Intent.createChooser(shareIntent, "Share Via"))

URI 노출을 무시하도록 내버려 두십시오.생성 후 추가

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

언급URL : https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed

반응형