على الأجهزة التي تعمل بالإصدار 4.4 من نظام التشغيل Android (المستوى 19 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يمكن لتطبيقك التفاعل مع مزوّد مستندات، بما في ذلك وحدات تخزين خارجية ومساحة تخزين مستندة إلى السحابة الإلكترونية، وذلك باستخدام إطار عمل Storage Access Framework. يتيح هذا الإطار للمستخدمين التفاعل مع أداة اختيار النظام لاختيار موفّر مستندات واختيار مستندات وملفات أخرى معيّنة ليتمكّن تطبيقك من إنشائها أو فتحها أو تعديلها.
بما أنّ المستخدم يشارك في اختيار الملفات أو الدلائل التي يمكن لتطبيقك الوصول إليها، لا تتطلّب هذه الآلية أي أذونات نظام، كما أنّها تعزّز تحكّم المستخدم في البيانات وخصوصيته. بالإضافة إلى ذلك، تظل هذه الملفات، المخزَّنة خارج دليل خاص بالتطبيق وخارج مكتبة الوسائط، على الجهاز بعد إلغاء تثبيت تطبيقك.
يتضمّن استخدام إطار العمل الخطوات التالية:
- يستدعي التطبيق هدفًا يتضمّن إجراءً ذا صلة بالتخزين. يتوافق هذا الإجراء مع حالة استخدام معيّنة يتيحها إطار العمل.
- يظهر للمستخدم أداة اختيار النظام، ما يتيح له تصفُّح موفّر المستندات واختيار موقع جغرافي أو مستند يتم فيه تنفيذ الإجراء المتعلّق بالتخزين.
- يحصل التطبيق على إذن القراءة والكتابة في معرّف الموارد المنتظم (URI) الذي يمثّل الموقع الجغرافي أو المستند الذي اختاره المستخدم. باستخدام عنوان URI هذا، يمكن للتطبيق تنفيذ عمليات على الموقع الجغرافي المحدّد.
لإتاحة الوصول إلى ملفات الوسائط على الأجهزة التي تعمل بالإصدار 9 من نظام التشغيل Android (المستوى 28 لواجهة برمجة التطبيقات) أو الإصدارات الأقدم، يجب الإفصاح عن إذن
READ_EXTERNAL_STORAGE
وضبط maxSdkVersion
على 28
.
يوضّح هذا الدليل حالات الاستخدام المختلفة التي يتيحها إطار العمل للتعامل مع الملفات والمستندات الأخرى. توضّح هذه الصفحة أيضًا كيفية تنفيذ عمليات على الموقع الجغرافي الذي يحدّده المستخدم.
حالات استخدام الوصول إلى المستندات والملفات الأخرى
يتيح "إطار عمل أذونات الوصول إلى مساحة التخزين" حالات الاستخدام التالية للوصول إلى الملفات والمستندات الأخرى.
- إنشاء ملف جديد
- يتيح إجراء
ACTION_CREATE_DOCUMENT
النية للمستخدمين حفظ ملف في موقع محدّد. - فتح مستند أو ملف
- يتيح إجراء الغرض
ACTION_OPEN_DOCUMENT
للمستخدمين اختيار مستند أو ملف معيّن لفتحه. - منح إذن الوصول إلى محتوى دليل يتيح إجراء
- The
ACTION_OPEN_DOCUMENT_TREE
intent، المتوفّر على نظام التشغيل Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث، للمستخدمين اختيار دليل معيّن، ما يمنح تطبيقك إذن الوصول إلى جميع الملفات والأدلة الفرعية داخل هذا الدليل.
تقدّم الأقسام التالية إرشادات حول كيفية ضبط كل حالة استخدام.
إنشاء ملف جديد
استخدِم إجراء الغرض
ACTION_CREATE_DOCUMENT
لتحميل أداة اختيار ملفات النظام والسماح للمستخدم باختيار
موقع جغرافي لكتابة محتوى ملف فيه. تشبه هذه العملية تلك المستخدمة في مربّعات الحوار "حفظ باسم" التي تستخدمها أنظمة التشغيل الأخرى.
ملاحظة: لا يمكن لـ ACTION_CREATE_DOCUMENT
استبدال ملف حالي. إذا حاول تطبيقك حفظ ملف بالاسم نفسه، سيضيف النظام رقمًا بين قوسين في نهاية اسم الملف.
على سبيل المثال، إذا حاول تطبيقك حفظ ملف باسم confirmation.pdf
في دليل يتضمّن ملفًا بهذا الاسم، سيحفظ النظام الملف الجديد بالاسم confirmation(1).pdf
.
عند ضبط الإجراء، حدِّد اسم الملف ونوع MIME، ويمكنك اختياريًا تحديد معرّف الموارد المنتظم (URI) للملف أو الدليل الذي يجب أن يعرضه أداة اختيار الملفات عند تحميلها لأول مرة باستخدام الإضافة EXTRA_INITIAL_URI
الخاصة بالإجراء.
يوضّح مقتطف الرمز البرمجي التالي كيفية إنشاء الهدف واستدعائه لإنشاء ملف:
Kotlin
// Request code for creating a PDF document. const val CREATE_FILE = 1 private fun createFile(pickerInitialUri: Uri) { val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "application/pdf" putExtra(Intent.EXTRA_TITLE, "invoice.pdf") // Optionally, specify a URI for the directory that should be opened in // the system file picker before your app creates the document. putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri) } startActivityForResult(intent, CREATE_FILE) }
Java
// Request code for creating a PDF document. private static final int CREATE_FILE = 1; private void createFile(Uri pickerInitialUri) { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("application/pdf"); intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf"); // Optionally, specify a URI for the directory that should be opened in // the system file picker when your app creates the document. intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri); startActivityForResult(intent, CREATE_FILE); }
فتح ملف
قد يستخدم تطبيقك المستندات كوحدة تخزين يدخل فيها المستخدمون البيانات التي يريدون مشاركتها مع الآخرين أو استيرادها إلى مستندات أخرى. وتشمل بعض الأمثلة فتح مستند إنتاجي أو فتح كتاب محفوظ كملف EPUB.
في هذه الحالات، اسمح للمستخدم باختيار الملف الذي يريد فتحه من خلال استدعاء الغرض ACTION_OPEN_DOCUMENT
، الذي يفتح تطبيق اختيار الملفات التابع للنظام. لعرض أنواع الملفات التي يتوافق معها تطبيقك فقط، حدِّد نوع MIME. يمكنك أيضًا بشكل اختياري تحديد معرّف الموارد المنتظم (URI) للملف الذي يجب أن يعرضه أداة اختيار الملفات عند تحميلها لأول مرة باستخدام الإضافة EXTRA_INITIAL_URI
في Intent.
يوضّح مقتطف الرمز البرمجي التالي كيفية إنشاء نية فتح مستند PDF واستدعائها:
Kotlin
// Request code for selecting a PDF document. const val PICK_PDF_FILE = 2 fun openFile(pickerInitialUri: Uri) { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "application/pdf" // Optionally, specify a URI for the file that should appear in the // system file picker when it loads. putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri) } startActivityForResult(intent, PICK_PDF_FILE) }
Java
// Request code for selecting a PDF document. private static final int PICK_PDF_FILE = 2; private void openFile(Uri pickerInitialUri) { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("application/pdf"); // Optionally, specify a URI for the file that should appear in the // system file picker when it loads. intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri); startActivityForResult(intent, PICK_PDF_FILE); }
قيود الوصول
في الإصدار 11 من نظام التشغيل Android (المستوى 30 لواجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام إجراء النية ACTION_OPEN_DOCUMENT
لطلب أن يختار المستخدم ملفات فردية من الأدلة التالية:
- دليل
Android/data/
وجميع الأدلة الفرعية - دليل
Android/obb/
وجميع الأدلة الفرعية
منح الإذن بالوصول إلى محتوى دليل
تتعامل تطبيقات إدارة الملفات وإنشاء الوسائط عادةً مع مجموعات من الملفات في تسلسل هرمي للمجلدات. لتوفير هذه الإمكانية في تطبيقك، استخدِم إجراء النية
ACTION_OPEN_DOCUMENT_TREE
الذي يتيح للمستخدم منح إذن الوصول إلى شجرة دليل بأكملها،
مع بعض الاستثناءات بدءًا من Android 11 (المستوى 30 لواجهة برمجة التطبيقات). يمكن لتطبيقك بعد ذلك الوصول إلى أي ملف في الدليل المحدّد وأي من أدلته الفرعية.
عند استخدام ACTION_OPEN_DOCUMENT_TREE
، لا يمكن لتطبيقك الوصول إلا إلى الملفات الموجودة في الدليل الذي يختاره المستخدم. ليس لديك إذن الوصول إلى ملفات التطبيقات الأخرى التي تقع خارج هذا الدليل الذي اختاره المستخدم. يتيح هذا الوصول الذي يتحكّم فيه المستخدمون للمستخدمين اختيار المحتوى الذي يناسبهم مشاركته مع تطبيقك.
يمكنك اختياريًا تحديد معرّف الموارد المنتظم (URI) للدليل الذي يجب أن يعرضه أداة اختيار الملفات عند تحميلها لأول مرة باستخدام الإضافة EXTRA_INITIAL_URI
الخاصة بغرض التنفيذ.
يوضّح مقتطف الرمز التالي كيفية إنشاء واستدعاء الغرض لفتح دليل:
Kotlin
fun openDirectory(pickerInitialUri: Uri) { // Choose a directory using the system's file picker. val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { // Optionally, specify a URI for the directory that should be opened in // the system file picker when it loads. putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri) } startActivityForResult(intent, your-request-code) }
Java
public void openDirectory(Uri uriToLoad) { // Choose a directory using the system's file picker. Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); // Optionally, specify a URI for the directory that should be opened in // the system file picker when it loads. intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uriToLoad); startActivityForResult(intent, your-request-code); }
قيود الوصول
في الإصدار 11 من نظام التشغيل Android (المستوى 30 لواجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام إجراء النية ACTION_OPEN_DOCUMENT_TREE
لطلب الوصول إلى الدلائل التالية:
- الدليل الجذر لوحدة التخزين الداخلية
- تمثّل هذه السمة الدليل الجذر لكل وحدة تخزين على بطاقة SD يرى مصنّع الجهاز أنّها موثوقة، بغض النظر عمّا إذا كانت البطاقة محاكية أو قابلة للإزالة. والمقصود بوحدة التخزين الموثوقة هي تلك التي يمكن للتطبيق الوصول إليها بنجاح في معظم الأوقات.
- دليل
Download
بالإضافة إلى ذلك، في نظام التشغيل Android 11 (المستوى 30 لواجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام إجراء النية ACTION_OPEN_DOCUMENT_TREE
لطلب أن يختار المستخدم ملفات فردية من الأدلة التالية:
- دليل
Android/data/
وجميع الأدلة الفرعية - دليل
Android/obb/
وجميع الأدلة الفرعية
تنفيذ عمليات على الموقع الجغرافي الذي تم اختياره
بعد أن يختار المستخدم ملفًا أو دليلًا باستخدام أداة اختيار الملفات في النظام، يمكنك استرداد معرّف الموارد الموحّد (URI) للعنصر المحدّد باستخدام الرمز التالي في onActivityResult()
:
Kotlin
override fun onActivityResult( requestCode: Int, resultCode: Int, resultData: Intent?) { if (requestCode == your-request-code && resultCode == Activity.RESULT_OK) { // The result data contains a URI for the document or directory that // the user selected. resultData?.data?.also { uri -> // Perform operations on the document using its URI. } } }
Java
@Override public void onActivityResult(int requestCode, int resultCode, Intent resultData) { if (requestCode == your-request-code && resultCode == Activity.RESULT_OK) { // The result data contains a URI for the document or directory that // the user selected. Uri uri = null; if (resultData != null) { uri = resultData.getData(); // Perform operations on the document using its URI. } } }
من خلال الحصول على مرجع إلى معرّف الموارد الموحّد (URI) الخاص بالعنصر المحدّد، يمكن لتطبيقك تنفيذ عدة عمليات على العنصر. على سبيل المثال، يمكنك الوصول إلى البيانات الوصفية للعنصر وتعديل العنصر في مكانه وحذفه.
توضّح الأقسام التالية كيفية إكمال الإجراءات على الملفات التي يختارها المستخدم.
تحديد العمليات التي يتيحها موفّر الخدمة
تسمح الجهات المختلفة التي تقدّم المحتوى بإجراء عمليات مختلفة على المستندات، مثل نسخ المستند أو عرض الصورة المصغّرة للمستند. لتحديد العمليات التي يتيحها مقدّم خدمة معيّن، تحقَّق من قيمة Document.COLUMN_FLAGS
.
يمكن لواجهة مستخدم تطبيقك بعد ذلك عرض الخيارات التي يتيحها مقدّم الخدمة فقط.
الاحتفاظ بالأذونات
عندما يفتح تطبيقك ملفًا للقراءة أو الكتابة، يمنح النظام تطبيقك إذنًا بالوصول إلى الملف باستخدام معرّف الموارد المنتظم (URI)، ويستمر هذا الإذن حتى إعادة تشغيل جهاز المستخدم. لنفترض أنّ تطبيقك هو تطبيق لتعديل الصور، وأنّك تريد أن يتمكّن المستخدمون من الوصول إلى آخر 5 صور عدّلوها مباشرةً من تطبيقك. إذا تمت إعادة تشغيل جهاز المستخدم، عليك توجيهه إلى أداة اختيار الملفات في النظام للعثور على الملفات.
للحفاظ على إمكانية الوصول إلى الملفات عند إعادة تشغيل الجهاز ولتقديم تجربة أفضل للمستخدم، يمكن لتطبيقك "الحصول" على إذن URI قابل للاستمرار يوفّره النظام، كما هو موضّح في مقتطف الرمز التالي:
Kotlin
val contentResolver = applicationContext.contentResolver val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION // Check for the freshest data. contentResolver.takePersistableUriPermission(uri, takeFlags)
Java
final int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); // Check for the freshest data. getContentResolver().takePersistableUriPermission(uri, takeFlags);
فحص البيانات الوصفية للمستند
عندما يكون لديك معرّف الموارد الموحّد لمستند، يمكنك الوصول إلى بياناته الوصفية. يسترد هذا المقتطف البيانات الوصفية لمستند محدّد بواسطة URI، ويسجّلها:
Kotlin
val contentResolver = applicationContext.contentResolver fun dumpImageMetaData(uri: Uri) { // The query, because it only applies to a single document, returns only // one row. There's no need to filter, sort, or select fields, // because we want all fields for one document. val cursor: Cursor? = contentResolver.query( uri, null, null, null, null, null) cursor?.use { // moveToFirst() returns false if the cursor has 0 rows. Very handy for // "if there's anything to look at, look at it" conditionals. if (it.moveToFirst()) { // Note it's called "Display Name". This is // provider-specific, and might not necessarily be the file name. val displayName: String = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME)) Log.i(TAG, "Display Name: $displayName") val sizeIndex: Int = it.getColumnIndex(OpenableColumns.SIZE) // If the size is unknown, the value stored is null. But because an // int can't be null, the behavior is implementation-specific, // and unpredictable. So as // a rule, check if it's null before assigning to an int. This will // happen often: The storage API allows for remote files, whose // size might not be locally known. val size: String = if (!it.isNull(sizeIndex)) { // Technically the column stores an int, but cursor.getString() // will do the conversion automatically. it.getString(sizeIndex) } else { "Unknown" } Log.i(TAG, "Size: $size") } } }
Java
public void dumpImageMetaData(Uri uri) { // The query, because it only applies to a single document, returns only // one row. There's no need to filter, sort, or select fields, // because we want all fields for one document. Cursor cursor = getActivity().getContentResolver() .query(uri, null, null, null, null, null); try { // moveToFirst() returns false if the cursor has 0 rows. Very handy for // "if there's anything to look at, look at it" conditionals. if (cursor != null && cursor.moveToFirst()) { // Note it's called "Display Name". This is // provider-specific, and might not necessarily be the file name. String displayName = cursor.getString( cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); Log.i(TAG, "Display Name: " + displayName); int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE); // If the size is unknown, the value stored is null. But because an // int can't be null, the behavior is implementation-specific, // and unpredictable. So as // a rule, check if it's null before assigning to an int. This will // happen often: The storage API allows for remote files, whose // size might not be locally known. String size = null; if (!cursor.isNull(sizeIndex)) { // Technically the column stores an int, but cursor.getString() // will do the conversion automatically. size = cursor.getString(sizeIndex); } else { size = "Unknown"; } Log.i(TAG, "Size: " + size); } } finally { cursor.close(); } }
فتح مستند
من خلال الحصول على مرجع إلى معرّف الموارد الموحّد (URI) الخاص بمستند، يمكنك فتح مستند لمزيد من المعالجة. يعرض هذا القسم أمثلة على فتح صورة نقطية وتدفّق إدخال.
صورة نقطية
يوضّح مقتطف الرمز التالي كيفية فتح ملف Bitmap
باستخدام معرّف الموارد الموحّد (URI) الخاص به:
Kotlin
val contentResolver = applicationContext.contentResolver @Throws(IOException::class) private fun getBitmapFromUri(uri: Uri): Bitmap { val parcelFileDescriptor: ParcelFileDescriptor = contentResolver.openFileDescriptor(uri, "r") val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor val image: Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor) parcelFileDescriptor.close() return image }
Java
private Bitmap getBitmapFromUri(Uri uri) throws IOException { ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r"); FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); parcelFileDescriptor.close(); return image; }
بعد فتح الصورة النقطية، يمكنك عرضها في
ImageView
.
البث الإدخالي
يوضّح مقتطف الرمز التالي كيفية فتح عنصر InputStream مع تحديد معرّف الموارد المنتظم (URI) الخاص به. في هذا المقتطف، تتم قراءة أسطر الملف في سلسلة:
Kotlin
val contentResolver = applicationContext.contentResolver @Throws(IOException::class) private fun readTextFromUri(uri: Uri): String { val stringBuilder = StringBuilder() contentResolver.openInputStream(uri)?.use { inputStream -> BufferedReader(InputStreamReader(inputStream)).use { reader -> var line: String? = reader.readLine() while (line != null) { stringBuilder.append(line) line = reader.readLine() } } } return stringBuilder.toString() }
Java
private String readTextFromUri(Uri uri) throws IOException { StringBuilder stringBuilder = new StringBuilder(); try (InputStream inputStream = getContentResolver().openInputStream(uri); BufferedReader reader = new BufferedReader( new InputStreamReader(Objects.requireNonNull(inputStream)))) { String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); } } return stringBuilder.toString(); }
تعديل مستند
يمكنك استخدام Storage Access Framework لتعديل مستند نصي في مكانه.
يستبدل مقتطف الرمز التالي محتويات المستند الممثَّل بمعرّف الموارد المنتظم (URI) المحدّد:
Kotlin
val contentResolver = applicationContext.contentResolver private fun alterDocument(uri: Uri) { try { contentResolver.openFileDescriptor(uri, "w")?.use { FileOutputStream(it.fileDescriptor).use { it.write( ("Overwritten at ${System.currentTimeMillis()}\n") .toByteArray() ) } } } catch (e: FileNotFoundException) { e.printStackTrace() } catch (e: IOException) { e.printStackTrace() } }
Java
private void alterDocument(Uri uri) { try { ParcelFileDescriptor pfd = getActivity().getContentResolver(). openFileDescriptor(uri, "w"); FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor()); fileOutputStream.write(("Overwritten at " + System.currentTimeMillis() + "\n").getBytes()); // Let the document provider know you're done by closing the stream. fileOutputStream.close(); pfd.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
حذف مستند
إذا كان لديك معرّف الموارد الموحّد (URI) الخاص بمستند وكان المستند
Document.COLUMN_FLAGS
يحتوي على
SUPPORTS_DELETE
،
يمكنك حذف المستند. مثلاً:
Kotlin
DocumentsContract.deleteDocument(applicationContext.contentResolver, uri)
Java
DocumentsContract.deleteDocument(applicationContext.contentResolver, uri);
استرداد معرّف موارد منتظم (URI) مكافئ للوسائط
توفّر طريقة
getMediaUri()
معرّف موارد منتظم (URI) لمتجر الوسائط يكون مكافئًا لمعرّف الموارد المنتظم لموفّر المستندات المحدّد. يشير معرّفا الموارد المنتظم (URI) إلى العنصر الأساسي نفسه. باستخدام معرّف الموارد الموحّد الخاص بمخزن الوسائط، يمكنك الوصول إلى ملفات الوسائط من مساحة التخزين المشتركة بسهولة أكبر.
تتيح طريقة getMediaUri()
استخدام معرّفات الموارد المنتظمة (URI) ExternalStorageProvider
. في نظام التشغيل Android 12 (المستوى 31 لواجهة برمجة التطبيقات) والإصدارات الأحدث، تتوافق الطريقة أيضًا مع معرّفات الموارد الموحّدة (URI) التي تبدأ بـ MediaDocumentsProvider
.
فتح ملف افتراضي
في نظام التشغيل Android 7.0 (المستوى 25 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يمكن لتطبيقك الاستفادة من الملفات الافتراضية التي يتيحها "إطار عمل الوصول إلى مساحة التخزين". على الرغم من أنّ الملفات الافتراضية ليس لها تمثيل ثنائي، يمكن لتطبيقك فتح محتواها من خلال تحويلها إلى نوع ملف مختلف أو عرض هذه الملفات باستخدام إجراء الغرض ACTION_VIEW
.
لفتح الملفات الافتراضية، يجب أن يتضمّن تطبيق العميل منطقًا خاصًا للتعامل معها. إذا أردت الحصول على تمثيل بايت للملف، مثلاً لمعاينة الملف، عليك طلب نوع MIME بديل من موفّر المستندات.
بعد أن يحدّد المستخدم اختيارًا، استخدِم معرّف الموارد المنتظم في بيانات النتائج لتحديد ما إذا كان الملف افتراضيًا، كما هو موضّح في مقتطف الرمز التالي:
Kotlin
private fun isVirtualFile(uri: Uri): Boolean { if (!DocumentsContract.isDocumentUri(this, uri)) { return false } val cursor: Cursor? = contentResolver.query( uri, arrayOf(DocumentsContract.Document.COLUMN_FLAGS), null, null, null ) val flags: Int = cursor?.use { if (cursor.moveToFirst()) { cursor.getInt(0) } else { 0 } } ?: 0 return flags and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0 }
Java
private boolean isVirtualFile(Uri uri) { if (!DocumentsContract.isDocumentUri(this, uri)) { return false; } Cursor cursor = getContentResolver().query( uri, new String[] { DocumentsContract.Document.COLUMN_FLAGS }, null, null, null); int flags = 0; if (cursor.moveToFirst()) { flags = cursor.getInt(0); } cursor.close(); return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0; }
بعد التأكّد من أنّ المستند هو ملف افتراضي، يمكنك بعد ذلك تحويل الملف إلى نوع MIME بديل، مثل "image/png"
. يوضّح مقتطف الرمز البرمجي التالي كيفية التحقّق مما إذا كان يمكن تمثيل ملف افتراضي كصورة، وفي حال كان ذلك ممكنًا، يتم الحصول على دفق إدخال من الملف الافتراضي:
Kotlin
@Throws(IOException::class) private fun getInputStreamForVirtualFile( uri: Uri, mimeTypeFilter: String): InputStream { val openableMimeTypes: Array<String>? = contentResolver.getStreamTypes(uri, mimeTypeFilter) return if (openableMimeTypes?.isNotEmpty() == true) { contentResolver .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null) .createInputStream() } else { throw FileNotFoundException() } }
Java
private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter) throws IOException { ContentResolver resolver = getContentResolver(); String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter); if (openableMimeTypes == null || openableMimeTypes.length < 1) { throw new FileNotFoundException(); } return resolver .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null) .createInputStream(); }
مراجع إضافية
لمزيد من المعلومات حول كيفية تخزين المستندات والملفات الأخرى والوصول إليها، يُرجى الرجوع إلى المراجع التالية.
نماذج
- ActionOpenDocument، المتوفرة على GitHub
- ActionOpenDocumentTree، المتوفر على GitHub