android यूआरआई से एक्सिफ डेटा कैसे प्राप्त करें इस पर एक अंतिम जवाब




android-intent uri (3)

इस विषय पर कई प्रश्नों पर चर्चा की गई है, जिसमें ज्यादातर अलग-अलग परिणाम हैं और एपीआई परिवर्तन और विभिन्न प्रकार के यूआरआई के कारण, कोई निश्चित उत्तर नहीं है

मेरे पास जवाब नहीं है, लेकिन चलिए इसके बारे में बात करते हैं। ExifInterface में एक एकल कन्स्ट्रक्टर है जो filePath स्वीकार करता है। यह स्वयं कष्टप्रद है, क्योंकि अब पथ पर भरोसा करने के लिए इसे निराश किया गया है - आपको Uri और ContentResolver उपयोग करना चाहिए। ठीक।

हमारे Uri नामित uri को onActivityResult में इरादे से पुनर्प्राप्त किया जा सकता है (यदि आप गैलरी से ACTION_GET_CONTENT साथ चित्र ACTION_GET_CONTENT ) या एक Uri हो सकता है जिसे हमने पहले किया था (यदि आप कैमरे से तस्वीर चुनते हैं और intent.putExtra(MediaStore.EXTRA_OUTPUT, uri) कॉल करते हैं intent.putExtra(MediaStore.EXTRA_OUTPUT, uri) )।

एपीआई <19

हमारे uri में दो अलग-अलग स्कीमा हो सकते हैं:

  • कैमरों से आने वाले उरीस में ज्यादातर file:// स्कीमा। वे इलाज के लिए बहुत आसान हैं, क्योंकि वे पथ पकड़ते हैं। आप new ExifInterface(uri.getPath()) को कॉल कर सकते हैं और आप कर चुके हैं।
  • गैलरी या अन्य सामग्री प्रदाताओं से आने वाले उर आमतौर पर एक content:// इंटरफ़ेस। मैं व्यक्तिगत रूप से नहीं जानता कि यह क्या है, लेकिन मुझे पागल कर रहा है।

जहां तक ​​मैं समझता हूं, यह दूसरा मामला, ContentResolver साथ व्यवहार किया जाना चाहिए जिसे आप Context.getContentResolver() साथ प्राप्त कर सकते हैं। किसी भी मामले में मैंने परीक्षण किए गए सभी ऐप्स के साथ निम्नलिखित कार्य :

public static ExifInterface getPictureData(Context context, Uri uri) {
    String[] uriParts = uri.toString().split(":");
    String path = null;

    if (uriParts[0].equals("content")) {
        // we can use ContentResolver.
        // let’s query the DATA column which holds the path
        String col = MediaStore.Images.ImageColumns.DATA;
        Cursor c = context.getContentResolver().query(uri,
                new String[]{col},
                null, null, null);

        if (c != null && c.moveToFirst()) {
            path = c.getString(c.getColumnIndex(col));
            c.close();
            return new ExifInterface(path);
        }

    } else if (uriParts[0].equals("file")) {
        // it's easy to get the path
        path = uri.getEncodedPath();
        return new ExifInterface(path);
    }
    return null;
}

API19 +

मेरी समस्या content:// साथ किटकैट से आगे बढ़ती content:// यूआरआई। किटकैट एक नए इरादे, ACTION_OPEN_DOCUMENT , और प्लेटफ़ॉर्म पिकर के साथ Storage Access Framework ( here देखें) प्रस्तुत करता है। हालांकि, ऐसा कहा जाता है कि

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

ACTION_OPEN_DOCUMENT का उद्देश्य ACTION_GET_CONTENT के प्रतिस्थापन का इरादा नहीं है। आपको जिस व्यक्ति का उपयोग करना चाहिए वह आपके ऐप की ज़रूरतों पर निर्भर करता है।

तो यह बहुत आसान रखने के लिए, ACTION_GET_CONTENT लीजिए कि हम पुराने ACTION_GET_CONTENT साथ ठीक हैं: यह एक चयनकर्ता संवाद को आग ACTION_GET_CONTENT जहां आप गैलरी ऐप चुन सकते हैं।

हालांकि, सामग्री दृष्टिकोण अब और काम नहीं करता है। कभी-कभी यह किटकैट पर काम करता है, लेकिन उदाहरण के लिए लॉलीपॉप पर कभी काम नहीं करता है । मुझे नहीं पता कि वास्तव में क्या बदल गया है।

मैंने बहुत खोज की है और कोशिश की है; किटकैट के लिए विशेष रूप से लिया गया एक और तरीका है:

String wholeId = DocumentsContract.getDocumentId(uri);
String[] parts = wholeId.split(“:”);
String numberId = parts[1];

Cursor c = context.getContentResolver().query(
    // why external and not internal ?
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    new String[]{ col },
    MediaStore.Images.Media._ID + “=?”,
    new String[]{ numberId },
    null);

यह कभी-कभी काम करता है, लेकिन अन्य नहीं। विशेष रूप से, यह काम करता है जब wholeId image:2839 तरह कुछ है image:2839 , लेकिन स्पष्ट रूप से तोड़ता है जब wholeId बस एक संख्या है।

आप सिस्टम पिकर (यानी ACTION_OPEN_DOCUMENT साथ गैलरी को ACTION_OPEN_DOCUMENT ) का उपयोग करके इसे आजमा सकते हैं: यदि आप " ACTION_OPEN_DOCUMENT " से कोई छवि चुनते हैं, तो यह काम करता है; यदि आप "डाउनलोड" से कोई छवि चुनते हैं, तो यह टूट जाता है।

तो कैसे करें ?!

तात्कालिक उत्तर यह है कि आप नहीं करते हैं , आपको ओएस के नए संस्करण में सामग्री यूआरआई से फ़ाइल पथ नहीं मिलते हैं। यह कहा जा सकता है कि सभी सामग्री यूरी चित्रों या यहां तक ​​कि फ़ाइलों को इंगित नहीं करती है।

यह मेरे लिए बिल्कुल ठीक है, और सबसे पहले मैंने इससे बचने के लिए काम किया। लेकिन फिर, अगर हमें पथों का उपयोग नहीं करना चाहिए तो हमें ExifInterface क्लास का उपयोग कैसे करना चाहिए?

मुझे समझ में नहीं आता कि आधुनिक ऐप्स यह कैसे करते हैं - ओरिएंटेशन और मेटाडाटा ढूंढना एक समस्या है जिसका आप तुरंत सामना करते हैं, और ContentResolver उस अर्थ में किसी भी एपीआई की पेशकश नहीं करता है। आपके पास ContentResolver.openFileDescriptor() और समान सामान हैं, लेकिन मेटाडेटा पढ़ने के लिए कोई एपीआई नहीं है (जो वास्तव में उस फ़ाइल में है)। बाहरी पुस्तकालय हो सकते हैं जो एक स्ट्रीम से Exif सामान पढ़ते हैं, लेकिन मैं इसे हल करने के लिए सामान्य / प्लेटफ़ॉर्म तरीके के बारे में सोच रहा हूं।

मैंने Google के ओपन सोर्स ऐप में समान कोड की खोज की है, लेकिन कुछ भी नहीं मिला।



कुछ नमूना कोड के साथ alex.dorokhov के जवाब पर विस्तार करने के लिए। समर्थन पुस्तकालय जाने का एक शानदार तरीका है।

build.gradle

dependencies {
...    
compile "com.android.support:exifinterface:25.0.1"
...
}

उदाहरण कोड:

import android.support.media.ExifInterface;
...
try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) {
      ExifInterface exif = new ExifInterface(inputStream);
      int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
    } catch (IOException e) {
      e.printStackTrace();
    }

एक बार जब हमने एपीआई 25 (शायद 24+ पर भी एक समस्या) को लक्षित करना शुरू किया, तो एंड्रॉइड 7 पर एपीआई 1 का समर्थन करने के बाद मुझे यह तरीका करना पड़ा, अगर मैं एक यूआरआई में कैमरे के पास गया तो हमारा ऐप क्रैश हो जाएगा बस एक फाइल का संदर्भ। इसलिए मुझे कैमरे के इरादे को पास करने के लिए एक यूआरआई बनाना पड़ा।

FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileprovider", tempFile);

मुद्दा यह है कि फ़ाइल को यूआरआई को वास्तविक फ़ाइल पथ में बदलने के लिए संभव नहीं है (अस्थायी फ़ाइल पथ पर रखने के अलावा)।


किसी भी मामले में मैंने परीक्षण किए गए सभी ऐप्स के साथ निम्नलिखित कार्य:

यह केवल तभी काम करेगा जब Uri MediaStore से कुछ आ रहा है। अगर Uri किसी और चीज से आती है तो यह असफल हो जाएगी।

तात्कालिक उत्तर यह है कि आप नहीं करते हैं, आपको ओएस के नए संस्करण में सामग्री यूआरआई से फ़ाइल पथ नहीं मिलते हैं। यह कहा जा सकता है कि सभी सामग्री यूरी चित्रों या यहां तक ​​कि फ़ाइलों को इंगित नहीं करती है।

सही बात। मैंने इसे कई अवसरों पर इंगित किया है, जैसे कि here

अगर हमें पथ का उपयोग नहीं करना चाहिए तो हमें ExifInterface क्लास का उपयोग कैसे करना चाहिए?

आप नहीं करते EXIF शीर्षलेख प्राप्त करने के लिए अन्य कोड का उपयोग करें।

बाहरी पुस्तकालय हो सकते हैं जो एक स्ट्रीम से एक्फ सामान पढ़ते हैं, लेकिन मैं इसे हल करने के लिए सामान्य / प्लेटफ़ॉर्म तरीके के बारे में सोच रहा हूं।

बाहरी पुस्तकालयों का प्रयोग करें।

मैंने Google के ओपन सोर्स ऐप में समान कोड की खोज की है, लेकिन कुछ भी नहीं मिला।

आप एमएमएस ऐप में कुछ पाएंगे।





android-contentresolver