अन्य एप्लिकेशन को सामग्री साझा करने के लिए समर्थन FileProvider का उपयोग कैसे करें?


142

मैं एंड्रॉइड सपोर्ट लाइब्रेरी के फाइलप्रॉइडर का उपयोग करके बाहरी एप्लिकेशन के साथ एक आंतरिक फ़ाइल को सही ढंग से साझा नहीं करने (ओपेन न करने) का रास्ता खोज रहा हूं ।

डॉक्स पर उदाहरण के बाद,

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.android.supportv4.my_files"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/my_paths" />
</provider>

और अन्य ऐप्स के लिए फ़ाइल साझा करने के लिए ShareCompat का उपयोग इस प्रकार है:

ShareCompat.IntentBuilder.from(activity)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

काम नहीं करता है, क्योंकि FLAG_GRANT_READ_URI_PERMISSION केवल dataइरादे पर निर्दिष्ट उरी के लिए अनुमति देता है , EXTRA_STREAMअतिरिक्त का मान नहीं (जैसा कि निर्धारित किया गया था setStream)।

मैं निर्धारित करके समझौता सुरक्षा करने की कोशिश की android:exportedकरने के लिए trueप्रदाता के लिए है, लेकिन FileProviderआंतरिक रूप से जांच करता है कि खुद को निर्यात किया जाता है, जब ऐसा है, तो यह एक अपवाद फेंकता है।


7
+1 यह वास्तव में मूल प्रश्न लगता है - Google या StackOverflow पर कुछ भी नहीं है , जहाँ तक मैं बता सकता था।
फिल

यहाँ एक पोस्ट करने के लिए एक न्यूनतम FileProvider सेटअप प्राप्त करने के लिए एक न्यूनतम github उदाहरण परियोजना का उपयोग कर जा रहा है: stackoverflow.com/a/48103567/2162226 । यह उन चरणों को प्रदान करता है जिनके लिए स्थानीय स्टैंडअलोन परियोजना में फ़ाइलों को (परिवर्तन किए बिना) कॉपी करना है
जीन बो

जवाबों:


159

FileProviderसमर्थन पुस्तकालय से उपयोग करके आपको विशिष्ट उड़ी को पढ़ने के लिए अन्य एप्लिकेशन के लिए मैन्युअल रूप से अनुदान (रनटाइम पर) को अनुदान देना होगा। Context.grantUriPermission और Context.revokeUriPermission विधियों का उपयोग करें ।

उदाहरण के लिए:

//grant permision for app with package "packegeName", eg. before starting other app via intent
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

//revoke permisions
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

अंतिम उपाय के रूप में, यदि आप पैकेज नाम प्रदान नहीं कर सकते हैं तो आप उन सभी ऐप्स को अनुमति दे सकते हैं जो विशिष्ट इरादे को संभाल सकते हैं:

//grant permisions for all apps that can handle given intent
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
...
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

प्रलेखन के अनुसार वैकल्पिक विधि :

  • सेटडेटा () को कॉल करके कंटेंट URI को एक इंटेंट में रखें।
  • इसके बाद, Method को Intent.setFlags () या तो FLAG_GRANT_READ_URI_PERMISSION या FLAG_GRANT_WRITE_URI_PERMISSION या दोनों के साथ कॉल करें।
  • अंत में, किसी अन्य ऐप में आशय भेजें। सबसे अधिक बार, आप इसे setResult () कॉल करके करते हैं।

    एक आशय में दी गई अनुमतियाँ प्रभाव में रहती हैं जबकि प्राप्त गतिविधि का ढेर सक्रिय होता है। जब स्टैक खत्म हो जाता है, तो
    अनुमतियाँ स्वचालित रूप से हटा दी जाती हैं।
    किसी क्लाइंट ऐप में एक गतिविधि को दी गई अनुमतियाँ स्वचालित रूप
    से उस ऐप के अन्य घटकों तक विस्तारित होती हैं ।

Btw। अगर आपको जरूरत है, तो आप फ़ाइलप्रोइडर के स्रोत को कॉपी कर सकते हैं और attachInfoप्रदाता को निर्यात करने से रोकने के लिए इसे बदलने की विधि बदल सकते हैं ।


26
का उपयोग करते हुए grantUriPermissionविधि हम एप्लिकेशन हम करने के लिए अनुमति देना चाहते हैं के पैकेज के नाम उपलब्ध कराने के लिए की जरूरत है। हालाँकि, साझा करते समय, आमतौर पर हमें नहीं पता होता है कि साझा गंतव्य क्या अनुप्रयोग है।
रैंडी सुगियंटो 'युकू'

क्या होगा अगर हम पैकेज का नाम नहीं जानते हैं और चाहते हैं कि अन्य ऐप्स इसे खोलने में सक्षम हों
StuStirling

3
क्या आप नवीनतम समाधान @ लिको के लिए एक कार्य कोड प्रदान कर सकते हैं?
StErMi

2
वहाँ एक बग खुला ट्रैकिंग क्यों समर्थन FileProvider setFlags के साथ काम नहीं करता है, लेकिन अनुदान के साथ काम करता है? या यह एक बग नहीं है, इस मामले में यह बग कैसे नहीं है?
एनएमआर

1
इसके अलावा, मैंने पाया कि ShareUripat का उपयोग करके बनाई गई मंशा के लिए ग्रांटयूरीप्रेशन कॉल काम नहीं करती थी, लेकिन यह मैन्युअल रूप से आशय बनाते समय किया था।
स्टुइरलिंग

33

पूरी तरह से काम कर रहे कोड का नमूना कैसे आंतरिक एप्लिकेशन फ़ोल्डर से फ़ाइल साझा करें। Android 7 और Android 5 पर परीक्षण किया गया।

AndroidManifest.xml

</application>
   ....
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="android.getqardio.com.gmslocationtest"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
</application>

एक्सएमएल / provider_paths

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

कोड ही

    File imagePath = new File(getFilesDir(), "external_files");
    imagePath.mkdir();
    File imageFile = new File(imagePath.getPath(), "test.jpg");

    // Write data in your file

    Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile);

    Intent intent = ShareCompat.IntentBuilder.from(this)
                .setStream(uri) // uri from FileProvider
                .setType("text/html")
                .getIntent()
                .setAction(Intent.ACTION_VIEW) //Change if needed
                .setDataAndType(uri, "image/*")
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

   startActivity(intent);

1
के उपयोग ShareCompat.IntentBuilderऔर अंत में यूआरआई अनुमति पढ़ा मेरे लिए ऐसा किया।
कॉर्ड रेहान

एंड्रॉइड के अन्य संस्करणों के लिए ब्रेक
ओलिवर डिक्सन

@ ऑलिवरडिक्सन: कौन से? मैंने इसे एंड्रॉइड 6.0 और 9.0 पर परीक्षण किया।
वायलेट जिराफ

1
यह FileProviderउस काम का उपयोग करने पर एकमात्र उत्तर है । अन्य उत्तरों से इरादे गलत हो जाते हैं (वे उपयोग नहीं करते हैं ShareCompat.IntentBuilder), और यह आशय किसी भी सार्थक तरीके से बाहरी एप्लिकेशन द्वारा खुला नहीं है। मैंने अपना जवाब खोजने तक इस बकवास पर शाब्दिक रूप से अधिकांश दिन बिताए हैं।
वायलेट जिराफ

1
@VioletGiraffe मैं ShareCompat का उपयोग नहीं कर रहा हूं, लेकिन मेरा काम करता है। मेरा मुद्दा यह था कि मैं गलती से अपने चयनकर्ता के इरादे को पढ़ने की अनुमति प्रदान कर रहा था, जब मुझे अपने इरादे को पढ़ने की अनुमति देने की आवश्यकता थी, इससे पहले कि यह चयनकर्ता के इरादे से पारित हो गया, साथ ही साथ चयनकर्ता को भी। मुझे उम्मीद है कि इसका कोई अर्थ है। बस सुनिश्चित करें कि आप सही इरादे से पढ़ने की अनुमति दे रहे हैं।
रयान

23

यह समाधान ओएस 4.4 के बाद से मेरे लिए काम करता है। सभी उपकरणों पर काम करने के लिए मैंने पुराने उपकरणों के लिए वर्कअराउंड जोड़ा। यह सुनिश्चित करता है कि हमेशा सबसे सुरक्षित समाधान का उपयोग किया जाता है।

Manifest.xml:

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.package.name.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:

<paths>
    <files-path name="app_directory" path="directory/"/>
</paths>

जावा:

public static void sendFile(Context context) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("text/plain");
    String dirpath = context.getFilesDir() + File.separator + "directory";
    File file = new File(dirpath + File.separator + "file.txt");
    Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);
    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    // Workaround for Android bug.
    // grantUriPermission also needed for KITKAT,
    // see https://code.google.com/p/android/issues/detail?id=76683
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            context.grantUriPermission(packageName, attachmentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }
    if (intent.resolveActivity(context.getPackageManager()) != null) {
        context.startActivity(intent);
    }
}

public static void revokeFileReadPermission(Context context) {
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        String dirpath = context.getFilesDir() + File.separator + "directory";
        File file = new File(dirpath + File.separator + "file.txt");
        Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);
        context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
}

अनुमति को फिर से चालू किया गया है और फिर से चालू या चालू करें () खंड या गतिविधि के तरीके onDestroy () विधियों में बदल दिया गया है।


1
इस समाधान ने मेरे लिए काम किया, लेकिन जब मैंने इरादा जोड़ा तब ही मैंने इसे शुरू किया ।सेटडैटाएंडटेप (यूआरआई, "वीडियो / *"); BTW लापता अटैचमेंट
Uri

क्या आपका मतलब है कि fileसे परिवर्तन नहीं होगा onResumeकरने के लिए onDestroy?
CoolMind

16

चूंकि फिल मूल सवाल पर अपनी टिप्पणी में कहता है, यह अद्वितीय है और Google पर SO पर कोई अन्य जानकारी नहीं है, मैंने सोचा कि मुझे अपने परिणाम भी साझा करने चाहिए:

मेरे ऐप में FileProvider ने साझा इरादे का उपयोग करके फ़ाइलों को साझा करने के लिए बॉक्स से बाहर काम किया। कोई विशेष कॉन्फ़िगरेशन या कोड आवश्यक नहीं था, इससे परे FileProvider सेटअप करने के लिए। अपने मेनिफेस्ट.एक्सएलएम में मैंने रखा:

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.my.apps.package.files"
        android:exported="false"
        android:grantUriPermissions="true" >
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/my_paths" />
    </provider>

My_paths.xml में मेरे पास है:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="files" path="." />
</paths>

मेरे कोड में:

    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.setType("application/xml");

    Uri uri = FileProvider.getUriForFile(this, "com.my.apps.package.files", fileToShare);
    shareIntent.putExtra(Intent.EXTRA_STREAM, uri);

    startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.share_file)));

और मैं बिना किसी परेशानी के जीमेल और गूगल ड्राइव जैसे ऐप के साथ अपने ऐप्स को अपने स्टोर में स्टोर कर सकता हूं।


9
अफसोस की बात है कि यह काम नहीं करता है। वह चर fileToShare केवल तभी काम करता है जब फ़ाइल SDcard या बाहरी फ़ाइल सिस्टम पर मौजूद हो। FileProvider का उपयोग करने की बात यह है कि आप आंतरिक सिस्टम से सामग्री साझा कर सकते हैं।
कीथ कोनोली

यह स्थानीय भंडारण फ़ाइलों (Android 5+ पर) के साथ ठीक से काम करने लगता है। लेकिन मुझे एंड्रॉइड 4.
पीटरडक

15

जहां तक ​​मैं बता सकता हूं कि यह केवल एंड्रॉइड के नए संस्करणों पर काम करेगा, इसलिए आपको इसे करने का एक अलग तरीका पता लगाना होगा। यह समाधान मेरे लिए 4.4 पर काम करता है, लेकिन 4.0 या 2.3.3 पर नहीं, इसलिए यह किसी ऐप के लिए सामग्री साझा करने के लिए एक उपयोगी तरीका नहीं होगा जो किसी भी एंड्रॉइड डिवाइस पर चलने के लिए है।

मेनिफेस्ट में .xml:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.mydomain.myapp.SharingActivity"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

आप अधिकारियों को कैसे निर्दिष्ट करते हैं, इस पर ध्यान दें। आपको उस गतिविधि को निर्दिष्ट करना होगा जिससे आप URI बनाएंगे और शेयर इरादे को लॉन्च करेंगे, इस मामले में गतिविधि को साझाकरण कहा जाता है। यह आवश्यकता Google के डॉक्स से स्पष्ट नहीं है!

file_paths.xml:

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

सावधान रहें कि आप कैसे पथ निर्दिष्ट करते हैं। आपके निजी आंतरिक संग्रहण की जड़ में उपरोक्त चूक।

SharingActivity.java में:

Uri contentUri = FileProvider.getUriForFile(getActivity(),
"com.mydomain.myapp.SharingActivity", myFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(shareIntent, "Share with"));

इस उदाहरण में हम एक JPEG छवि साझा कर रहे हैं।

अंत में यह अपने आप को आश्वस्त करने के लिए एक अच्छा विचार है कि आपने फ़ाइल को ठीक से सहेजा है और आप इसे कुछ इस तरह से एक्सेस कर सकते हैं:

File myFile = getActivity().getFileStreamPath("mySavedImage.jpeg");
if(myFile != null){
    Log.d(TAG, "File found, file description: "+myFile.toString());
}else{
    Log.w(TAG, "File not found!");
}

8

मेरे ऐप में FileProvider ठीक काम करता है, और मैं जीमेल, याहू आदि जैसे ईमेल क्लाइंट के लिए फाइलों की निर्देशिका में संग्रहीत आंतरिक फ़ाइलों को संलग्न करने में सक्षम हूं।

मेरे प्रकट रूप में, मेरे द्वारा रखे गए Android प्रलेखन में उल्लेख किया गया है:

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.package.name.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>

और जैसे-जैसे मेरी फाइलें रूट फाइल डायरेक्टरी में जमा होती गईं, फाइलपाथेसएक्सएमएल इस प्रकार थे:

 <paths>
<files-path path="." name="name" />

अब कोड में:

 File file=new File(context.getFilesDir(),"test.txt");

 Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);

 shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                                     "Test");

 shareIntent.setType("text/plain");

 shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
                                 new String[] {"email-address you want to send the file to"});

   Uri uri = FileProvider.getUriForFile(context,"com.package.name.fileprovider",
                                                   file);

                ArrayList<Uri> uris = new ArrayList<Uri>();
                uris.add(uri);

                shareIntent .putParcelableArrayListExtra(Intent.EXTRA_STREAM,
                                                        uris);


                try {
                   context.startActivity(Intent.createChooser(shareIntent , "Email:").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));                                                      


                }
                catch(ActivityNotFoundException e) {
                    Toast.makeText(context,
                                   "Sorry No email Application was found",
                                   Toast.LENGTH_SHORT).show();
                }
            }

यह मेरे लिए काम किया है। यह मदद करता है :)


1
आप रास्तों को पोस्ट करने के लिए असली MVP = "।" मेरे लिए काम किया
robsstackoverflow

Im त्रुटि हो रही है जैसे "कॉन्फ़िगर किए गए रूट को खोजने में विफल रहा है जिसमें /" मैंने समान कोड का उपयोग किया है
रक्षिथ कुमार

5

यदि आपको कैमरे से एक छवि मिलती है, तो इनमें से कोई भी समाधान Android 4.4 के लिए काम नहीं करता है। इस मामले में संस्करणों की जांच करना बेहतर है।

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getContext().getPackageManager()) != null) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        uri = Uri.fromFile(file);
    } else {
        uri = FileProvider.getUriForFile(getContext(), getContext().getPackageName() + ".provider", file);
    }
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(intent, CAMERA_REQUEST);
}

1

बस ऊपर दिए गए उत्तर को बेहतर बनाने के लिए: यदि आपको NullPointerEx मिल रहा है:

तुम भी संदर्भ के बिना getApplicationContext () का उपयोग कर सकते हैं

                List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                for (ResolveInfo resolveInfo : resInfoList) {
                    String packageName = resolveInfo.activityInfo.packageName;
                    grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }

1

मैं कुछ ऐसा साझा करना चाहता हूं जिसने हमें कुछ दिनों के लिए अवरुद्ध कर दिया: फ़ाइलप्रोविडर कोड को एप्लिकेशन टैग के बीच डाला जाना चाहिए , इसके बाद नहीं। यह तुच्छ हो सकता है, लेकिन यह कभी भी निर्दिष्ट नहीं है, और मैंने सोचा कि मैं किसी की मदद कर सकता था! (फिर से piolo94 के लिए धन्यवाद)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.