Android: एक सामग्री URI से एक फ़ाइल URI प्राप्त करना?


133

मेरे ऐप में उपयोगकर्ता को एक ऑडियो फ़ाइल का चयन करना है जिसे ऐप तब संभालता है। समस्या यह है कि ऐप को ऑडियो फ़ाइलों के साथ क्या करना है, यह करने के लिए, मुझे फ़ाइल प्रारूप में URI की आवश्यकता है। जब मैं ऐप में ऑडियो फ़ाइल ब्राउज़ करने के लिए एंड्रॉइड के मूल संगीत प्लेयर का उपयोग करता हूं, तो यूआरआई एक सामग्री यूआरआई है, जो इस प्रकार है:

content://media/external/audio/media/710

हालाँकि, लोकप्रिय फ़ाइल प्रबंधक एप्लिकेशन एस्ट्रो का उपयोग करके, मुझे निम्नलिखित मिलते हैं:

file:///sdcard/media/audio/ringtones/GetupGetOut.mp3

उत्तरार्द्ध मेरे साथ काम करने के लिए बहुत अधिक सुलभ है, लेकिन निश्चित रूप से मैं चाहता हूं कि एप्लिकेशन को ऑडियो फ़ाइल के साथ कार्यक्षमता हो जो उपयोगकर्ता अपने संग्रह को ब्राउज़ करने के लिए उपयोग किए गए प्रोग्राम की परवाह किए बिना चुनता है। तो मेरा सवाल यह है कि क्या content://स्टाइल URI को URI में बदलने का कोई तरीका है file://? अन्यथा, आप मुझे इस समस्या को हल करने के लिए क्या सलाह देंगे? यहाँ वह कोड है जो चयनकर्ता को कॉल करता है, संदर्भ के लिए:

Intent ringIntent = new Intent();
ringIntent.setType("audio/mp3");
ringIntent.setAction(Intent.ACTION_GET_CONTENT);
ringIntent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(ringIntent, "Select Ringtone"), SELECT_RINGTONE);

मैं निम्नलिखित सामग्री यूआरआई के साथ करता हूं:

m_ringerPath = m_ringtoneUri.getPath();
File file = new File(m_ringerPath);

फिर कुछ FileInputStream सामान को फ़ाइल के साथ करें।


1
क्या आप कॉल कर रहे हैं जो सामग्री यूआरआई को पसंद नहीं है?
फिल लेलो

1
बहुत सारी सामग्री उरिस है जहां आपको फ़ाइल पथ नहीं मिल सकता है, क्योंकि सभी सामग्री उरिस में फ़ाइलपथ नहीं हैं। फ़ाइलपथ का उपयोग न करें।
मूइंग डक

जवाबों:


153

बस यूआरआई से getContentResolver().openInputStream(uri)प्राप्त करने के लिए उपयोग करें InputStream

http://developer.android.com/reference/android/content/ContentResolver.html#openInputStream(android.net.Uri)


12
चयनकर्ता गतिविधि से यूआरआई की योजना को आप तक लौटाएं। अगर uri.getScheme.equals ("सामग्री"), तो इसे सामग्री रिज़ॉल्वर से खोलें। अगर uri.Scheme.equals ("फ़ाइल"), सामान्य फ़ाइल विधियों का उपयोग करके इसे खोलें। किसी भी तरह से, आप एक इनपुटस्ट्रीम के साथ समाप्त होंगे जिसे आप सामान्य कोड का उपयोग करके संसाधित कर सकते हैं।
जेसन लेब्रन

16
वास्तव में, मैं बस getContentResolver ()। OpenInputStream () के लिए डॉक्स फिर से पढ़ता हूं, और यह "सामग्री" या "फ़ाइल" की योजनाओं के लिए स्वचालित रूप से काम करता है, इसलिए आपको योजना की जांच करने की आवश्यकता नहीं है ... यदि आप सुरक्षित रूप से कर सकते हैं। यह मान लें कि यह हमेशा संतुष्ट रहने वाला है: // या फ़ाइल: // तब openInputStream () हमेशा काम करेगा।
जेसन लेब्रन

57
क्या इनपुटस्ट्रीम (सामग्री से:) के बजाय फ़ाइल प्राप्त करने का एक तरीका है?
एलिकएल्ज़िन-किलाका

4
@kilaka आप फ़ाइल पा सकते हैं लेकिन यह दर्दनाक है। देखें stackoverflow.com/a/20559418/294855
डैनियल आयटेकिन

7
यह उत्तर किसी ऐसे व्यक्ति के लिए अपर्याप्त है जो एक बंद-स्रोत एपीआई का उपयोग कर रहा है जो फाइलस्ट्रीम के बजाय फाइलों पर निर्भर है, लेकिन फिर भी उपयोगकर्ता को फ़ाइल का चयन करने की अनुमति देने के लिए ओएस का उपयोग करना चाहता है। उत्तर @DanyalAytekin संदर्भित था कि वास्तव में मुझे क्या चाहिए था (और वास्तव में, मैं बहुत सारी वसा को ट्रिम करने में सक्षम था क्योंकि मुझे पता है कि मैं किस प्रकार की फाइलों के साथ काम कर रहा हूं)।
बंदर ०५०६

45

यह कुछ विशिष्ट सामग्री रिज़ॉल्वर दर्द बिंदुओं पर काबू पाने के पदावनत और हैक करने के तरीके के साथ एक पुराना उत्तर है। इसे नमक के कुछ विशाल दानों के साथ लें और यदि संभव हो तो उचित ओपनइंस्ट्रीमस्ट्रीम एपीआई का उपयोग करें।

URI file://से रास्ता पाने के लिए आप सामग्री रिज़ॉल्वर का उपयोग कर सकते हैं content://:

String filePath = null;
Uri _uri = data.getData();
Log.d("","URI = "+ _uri);                                       
if (_uri != null && "content".equals(_uri.getScheme())) {
    Cursor cursor = this.getContentResolver().query(_uri, new String[] { android.provider.MediaStore.Images.ImageColumns.DATA }, null, null, null);
    cursor.moveToFirst();   
    filePath = cursor.getString(0);
    cursor.close();
} else {
    filePath = _uri.getPath();
}
Log.d("","Chosen path = "+ filePath);

1
धन्यवाद, यह पूरी तरह से काम किया। मैं इनपुटस्ट्रीम का उपयोग नहीं कर सका जैसे कि स्वीकृत उत्तर बताता है।
ldam

4
यह केवल स्थानीय फ़ाइलों के लिए काम करता है, उदाहरण के लिए यह Google ड्राइव के लिए काम नहीं करता है
bluewhile

1
कभी-कभी काम करता है, कभी-कभी रिटर्न फाइल करता है: /// स्टोरेज / एमुलेटेड / 0 / ... जो मौजूद नहीं है।
रेजा मोहम्मदी

1
क्या कॉलम "_data" ( android.provider.MediaStore.Images.ImageColumns.DATA) हमेशा इस बात की गारंटी है कि अगर स्कीम है content://?
एडवर्ड फॉक

2
यह एक प्रमुख विरोधी पैटर्न है। कुछ ContentProviders उस स्तंभ को प्रदान करते हैं, लेकिन Fileजब आप ContentResolver को बायपास करने का प्रयास करते हैं, तो आपको पढ़ने / लिखने की पहुँच की गारंटी नहीं होती है । Uris पर काम करने के लिए ContentResolver विधियों का उपयोग करें content://, यह Google इंजीनियरों द्वारा प्रोत्साहित किया गया आधिकारिक तरीका है।
user1643723

9

यदि आपके पास एक सामग्री है, तो आपके साथ Uri content://com.externalstorage...इस विधि का उपयोग किसी फ़ोल्डर का पूर्ण पथ प्राप्त करने के लिए कर सकता है या Android 19 या इसके बाद के संस्करण पर फ़ाइल कर सकता है

public static String getPath(final Context context, final Uri uri) {
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        System.out.println("getPath() uri: " + uri.toString());
        System.out.println("getPath() uri authority: " + uri.getAuthority());
        System.out.println("getPath() uri path: " + uri.getPath());

        // ExternalStorageProvider
        if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            System.out.println("getPath() docId: " + docId + ", split: " + split.length + ", type: " + type);

            // This is for checking Main Memory
            if ("primary".equalsIgnoreCase(type)) {
                if (split.length > 1) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1] + "/";
                } else {
                    return Environment.getExternalStorageDirectory() + "/";
                }
                // This is for checking SD Card
            } else {
                return "storage" + "/" + docId.replace(":", "/");
            }

        }
    }
    return null;
}

आप चेक कर सकते हैं कि उरी का प्रत्येक भाग प्रिंटलाइन का उपयोग कर रहा है। मेरे एसडी कार्ड और डिवाइस मुख्य मेमोरी के लिए लौटाया गया मान नीचे सूचीबद्ध है। यदि फ़ाइल मेमोरी में है तो आप एक्सेस कर सकते हैं और हटा सकते हैं लेकिन मैं इस विधि का उपयोग करके एसडी कार्ड से फाइल को डिलीट करने में सक्षम नहीं था , केवल इस निरपेक्ष पथ का उपयोग करके डी या ईपिन छवि को पढ़ें। यदि आपके पास इस पद्धति का उपयोग करके हटाने का कोई उपाय है, तो कृपया साझा करें। एसडी कार्ड

getPath() uri: content://com.android.externalstorage.documents/tree/612E-B7BF%3A/document/612E-B7BF%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/612E-B7BF:/document/612E-B7BF:
getPath() docId: 612E-B7BF:, split: 1, type: 612E-B7BF

मुख्य स्मृति

getPath() uri: content://com.android.externalstorage.documents/tree/primary%3A/document/primary%3A
getPath() uri authority: com.android.externalstorage.documents
getPath() uri path: /tree/primary:/document/primary:
getPath() docId: primary:, split: 1, type: primary

यदि आप file:///पथ उपयोग प्राप्त करने के बाद उरी प्राप्त करना चाहते हैं

DocumentFile documentFile = DocumentFile.fromFile(new File(path));
documentFile.getUri() // will return a Uri with file Uri

मुझे नहीं लगता कि यह एक आवेदन में ऐसा करने का सही तरीका है। दुख की बात है कि मैं इसे एक
त्वरित

@Bondax हाँ, आपको फ़ाइल पथ या फ़ाइल Uris के बजाय सामग्री Uris के साथ काम करना चाहिए। इस तरह से स्टोरेज एक्सेस फ्रेमवर्क पेश किया गया है। लेकिन, यदि आप फ़ाइल उरई प्राप्त करना चाहते हैं, तो यह अन्य उत्तरों का सबसे सही तरीका है क्योंकि आप दस्तावेज़कंट्रैक्ट क्लास का उपयोग करते हैं। यदि आप Github में Google नमूनों की जांच करते हैं, तो आप देखेंगे कि वे इस वर्ग का उपयोग किसी फ़ोल्डर के उप-फ़ोल्डर प्राप्त करने के लिए भी करते हैं।
थ्रेशियन

और डॉक्यूमेंटफाइल क्लास भी एपी 19 में नया जोड़ है और आप एसएएफ से यूरिस का उपयोग कैसे करते हैं। सही तरीका यह है कि आप ऐप के लिए एक मानक पथ का उपयोग करें और उपयोगकर्ता को SAF ui के माध्यम से एक फ़ोल्डर के लिए अनुमति देने के लिए कहें, उड़ी स्ट्रिंग को साझा प्राथमिकताओं में सहेजें, और जब दस्तावेज़ फ़ोल्डर के साथ फ़ोल्डर तक पहुंच की आवश्यकता हो
Thracian

7

URI को सामग्री के साथ संभालने की कोशिश: // योजना को कॉल करके ContentResolver.query() एक अच्छा समाधान नहीं है। 4.2.2 पर चलने वाले एचटीसी डिजायर पर आप NULL को एक क्वेरी परिणाम के रूप में प्राप्त कर सकते हैं।

इसके बजाय ContentResolver का उपयोग क्यों नहीं किया जाता है? https://stackoverflow.com/a/29141800/3205334


लेकिन कभी-कभी हमें केवल मार्ग की आवश्यकता होती है। हमें वास्तव में फ़ाइल को मेमोरी में लोड नहीं करना है।
किमी चिउ

2
यदि आपके पास इसके लिए पहुँच अधिकार नहीं हैं तो "पथ" बेकार है। उदाहरण के लिए, यदि कोई एप्लिकेशन आपको एक content://uri देता है, तो यह निजी आंतरिक निर्देशिका में फ़ाइल करने के लिए संबंधित है, तो आप Fileनए Android संस्करणों में API के साथ उस uri का उपयोग नहीं कर पाएंगे । इस प्रकार की सुरक्षा सीमाओं को पार करने के लिए ContentResolver को डिज़ाइन किया गया है। अगर आपको ContentResolver से uri मिला है, तो आप इसे सिर्फ काम करने की उम्मीद कर सकते हैं।
user1643723

5

प्रेरित उत्तर जेसन लाब्रून और डार्थ रेवेन हैं । पहले से ही उत्तर दिए गए दृष्टिकोणों को आजमाकर मुझे नीचे दिए गए समाधान की ओर ले जाया जा सकता है, जो ज्यादातर कर्सर शून्य मामलों और सामग्री से रूपांतरण को कवर कर सकते हैं : // फाइल करने के लिए : //

फ़ाइल को परिवर्तित करने के लिए, फ़ाइल को प्राप्त यूरी से पढ़ें और लिखें

public static Uri getFilePathFromUri(Uri uri) throws IOException {
    String fileName = getFileName(uri);
    File file = new File(myContext.getExternalCacheDir(), fileName);
    file.createNewFile();
    try (OutputStream outputStream = new FileOutputStream(file);
         InputStream inputStream = myContext.getContentResolver().openInputStream(uri)) {
        FileUtil.copyStream(inputStream, outputStream); //Simply reads input to output stream
        outputStream.flush();
    }
    return Uri.fromFile(file);
}

फ़ाइल नाम का उपयोग करने के लिए, यह कर्सर अशक्त मामले को कवर करेगा

public static String getFileName(Uri uri) {
    String fileName = getFileNameFromCursor(uri);
    if (fileName == null) {
        String fileExtension = getFileExtension(uri);
        fileName = "temp_file" + (fileExtension != null ? "." + fileExtension : "");
    } else if (!fileName.contains(".")) {
        String fileExtension = getFileExtension(uri);
        fileName = fileName + "." + fileExtension;
    }
    return fileName;
}

एक्स्टेंशन फाइल करने के लिए माइम टाइप से कन्वर्ट करने का अच्छा विकल्प है

 public static String getFileExtension(Uri uri) {
    String fileType = myContext.getContentResolver().getType(uri);
    return MimeTypeMap.getSingleton().getExtensionFromMimeType(fileType);
}

फ़ाइल का नाम प्राप्त करने के लिए कर्सर

public static String getFileNameFromCursor(Uri uri) {
    Cursor fileCursor = myContext.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null);
    String fileName = null;
    if (fileCursor != null && fileCursor.moveToFirst()) {
        int cIndex = fileCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        if (cIndex != -1) {
            fileName = fileCursor.getString(cIndex);
        }
    }
    return fileName;
}

धन्यवाद, एक सप्ताह से इसे देख रहा था। इसके लिए फाइल कॉपी करना पसंद नहीं है लेकिन यह काम करता है।
justdan0227

3

वैसे मुझे जवाब देने में थोड़ी देर हो गई है, लेकिन मेरे कोड का परीक्षण किया गया है

uri से चेक योजना:

 byte[] videoBytes;

if (uri.getScheme().equals("content")){
        InputStream iStream =   context.getContentResolver().openInputStream(uri);
            videoBytes = getBytes(iStream);
        }else{
            File file = new File(uri.getPath());
            FileInputStream fileInputStream = new FileInputStream(file);     
            videoBytes = getBytes(fileInputStream);
        }

उपर्युक्त उत्तर में मैंने वीडियो uri को बाइट्स सरणी में बदल दिया है, लेकिन यह सवाल से संबंधित नहीं है, मैंने केवल अपने उपयोग को दिखाने के लिए अपना पूरा कोड कॉपी किया है FileInputStreamऔर InputStreamजैसा कि दोनों मेरे कोड में समान काम कर रहे हैं।

मैंने वैरिएबल संदर्भ का उपयोग किया है जो कि मेरे फ्रेग्मेंट में getActivity () है और एक्टिविटी में यह केवल ActivName.this हो सकता है

context=getActivity(); // खुशबू में

context=ActivityName.this;// गतिविधि में


मुझे पता है कि यह सवाल से संबंधित नहीं है, लेकिन आप इसका उपयोग कैसे करेंगे byte[] videoBytes;? अधिकांश उत्तर केवल यह दिखाते हैं कि InputStreamकिसी छवि के साथ कैसे उपयोग किया जाए।
केआरके
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.