على الأجهزة التي تعمل بنظام التشغيل Android 4.4 (مستوى واجهة برمجة التطبيقات 19) والإصدارات الأحدث، يمكن لتطبيقك التفاعل مع موفّر المستندات، بما في ذلك وحدات التخزين الخارجية والتخزين المستند إلى السحابة الإلكترونية، باستخدام Storage Access Framework. يسمح إطار العمل هذا للمستخدمين بالتفاعل مع أداة اختيار مضمّنة بالنظام لاختيار موفّر مستندات واختيار مستندات معيّنة وملفات أخرى ليتمكّن تطبيقك من إنشائها أو فتحها أو تعديلها.
بما أنّ المستخدم يشارك في اختيار الملفات أو الأدلة التي يمكن لتطبيقك الوصول إليها، لا تتطلّب هذه الآلية أي أذونات نظام، ويتم تعزيز تحكّم المستخدم وخصوصيته. بالإضافة إلى ذلك، تظل هذه الملفات، المخزّنة خارج دليل خاص بالتطبيق وخارج الوسائط المخزّنة، على الجهاز بعد إلغاء تثبيت تطبيقك.
يتضمّن استخدام الإطار الخطوات التالية:
- يستدعي التطبيق هدفًا يتضمّن إجراءً متعلقًا بالتخزين. يتوافق هذا الإجراء مع حالة الاستخدام المحدّدة التي يتيحها إطار العمل.
- تظهر للمستخدم أداة اختيار مضمّنة بالنظام، ما يسمح له بتصفّح موفّر مستندات واختيار موقع أو مستند يتم فيه تنفيذ الإجراء المتعلّق بالتخزين.
- يحصل التطبيق على إذن القراءة والكتابة لعنوان URI يمثّل الموقع أو المستند الذي اختاره المستخدم. باستخدام عنوان URI هذا، يمكن للتطبيق تنفيذ عمليات في الموقع الذي تم اختياره.
لإتاحة الوصول إلى ملفات الوسائط على الأجهزة التي تعمل بنظام التشغيل Android 9 (مستوى واجهة برمجة التطبيقات 28) أو
الإصدارات الأقدم، عليك الإعلان عن
READ_EXTERNAL_STORAGE
وضبط maxSdkVersion على 28.
يوضّح هذا الدليل حالات الاستخدام المختلفة التي يتيحها الإطار للتعامل مع الملفات والمستندات الأخرى. ويوضّح أيضًا كيفية تنفيذ العمليات في الموقع الذي اختاره المستخدم.
حالات استخدام للوصول إلى المستندات والملفات الأخرى
يتيح Storage Access Framework حالات الاستخدام التالية للوصول إلى الملفات والمستندات الأخرى.
- إنشاء ملف جديد
- يسمح إجراء الهدف
ACTION_CREATE_DOCUMENTللمستخدمين بحفظ ملف في موقع معيّن. - فتح مستند أو ملف
- يسمح
ACTION_OPEN_DOCUMENTإجراء الهدف للمستخدمين باختيار مستند أو ملف معيّن لفتحه. - منح إذن الوصول إلى محتويات دليل
- يسمح إجراء الهدف
ACTION_OPEN_DOCUMENT_TREE، المتوفّر على 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
للهدف.
يوضّح مقتطف الرمز التالي كيفية إنشاء الهدف واستدعاؤه لفتح مستند 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); }
قيود الوصول
على Android 11 (المستوى 30 من واجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام intent action 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); }
قيود الوصول
على Android 11 (المستوى 30 من واجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام intent action ACTION_OPEN_DOCUMENT_TREE لطلب الوصول إلى الأدلة التالية:
- الدليل الجذري لوحدة التخزين الداخلية
- الدليل الجذري لكل وحدة تخزين لبطاقة SD تعتبرها الشركة المصنّعة للجهاز موثوقة، بغض النظر عمّا إذا كانت البطاقة محاكاة أو قابلة للإزالة. وحدة التخزين الموثوقة هي وحدة يمكن للتطبيق الوصول إليها بنجاح في معظم الأوقات.
- دليل
Download
علاوةً على ذلك، على Android 11 (المستوى 30 من واجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام intent action 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 لمستند، يمكنك الوصول إلى بيانات تعريفه. يسترد مقتطف الرمز هذا بيانات تعريف مستند محدّد من خلال عنوان 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 لموفّر المستندات
المحدّد. يشير عنوانا URI إلى العنصر الأساسي نفسه. باستخدام عنوان URI للوسائط المخزّنة، يمكنك الوصول بسهولة أكبر إلى ملفات الوسائط من مساحة التخزين المشتركة.
يتيح الأسلوب getMediaUri() عناوين URI لـ ExternalStorageProvider. على Android 12 (المستوى 31 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يتيح الأسلوب أيضًا عناوين URI لـ MediaDocumentsProvider.
فتح ملف افتراضي
على Android 7.0 (مستوى واجهة برمجة التطبيقات 25) والإصدارات الأحدث، يمكن لتطبيقك استخدام الملفات الافتراضية التي يتيحها Storage Access Framework. على الرغم من أنّ الملفات الافتراضية ليس لها تمثيل ثنائي، يمكن لتطبيقك فتح محتوياتها من خلال تحويلها ضمنيًا إلى نوع ملف مختلف أو من خلال عرض هذه الملفات باستخدام ACTION_VIEW intent action.
لفتح الملفات الافتراضية، يحتاج تطبيق العميل إلى تضمين منطق خاص للتعامل معها. إذا كنت تريد الحصول على تمثيل بايت للملف، مثلاً لمعاينة الملف، عليك طلب نوع MIME بديل من موفّر المستندات.
بعد أن يختار المستخدم، استخدِم عنوان URI في بيانات النتائج لتحديد ما إذا كان الملف افتراضيًا، كما هو موضّح في مقتطف الرمز التالي:
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.