الوصول إلى المستندات والملفات الأخرى من مساحة التخزين المشتركة

يمكن لتطبيقك التفاعل على الأجهزة التي تعمل بالإصدار 4.4 من نظام التشغيل Android (المستوى 19 من واجهة برمجة التطبيقات) والإصدارات الأحدث. مع مزوِّد المستندات بما في ذلك وحدات التخزين الخارجية وسعة التخزين المستندة إلى السحابة الإلكترونية، باستخدام مساحة إطار عمل الوصول يتيح إطار العمل هذا للمستخدمين التفاعل مع أداة اختيار النظام اختيار مزوِّد المستندات وتحديد مستندات معيّنة وملفات أخرى لإنشاء تطبيقك أو فتحه أو تعديله

نظرًا لأن المستخدم يشارك في تحديد الملفات أو الأدلة التي يتضمنها تطبيقك الوصول إليها، لا تتطلب هذه الآلية أي نظام والأذونات والتحكم للمستخدم والخصوصية تحسين الأداء. بالإضافة إلى ذلك، يتم تخزين هذه الملفات خارج دليل التطبيق وخارجه، يظل على الجهاز بعد إلغاء تثبيت تطبيقك.

يتضمن استخدام إطار العمل الخطوات التالية:

  1. يستدعي التطبيق هدفًا يتضمّن إجراءً متعلقًا بمساحة التخزين. هذا الإجراء مع حالة استخدام معيّنة يقدّمها إطار العمل المتوفرة.
  2. يرى المستخدم أداة اختيار النظام، ما يسمح له بتصفّح موفِّر المستندات. واختَر موقعًا أو وثيقة يمكن تنفيذ الإجراء المتعلق بالتخزين فيها.
  3. يحصل التطبيق على إذن الوصول للقراءة والكتابة إلى معرِّف الموارد المنتظم (URI) الذي يمثّل معرّف المستخدم. الموقع أو الوثيقة التي اخترتها. باستخدام عنوان URI هذا، يمكن للتطبيق إجراء عمليات على للموقع الذي تم اختياره.

لإتاحة الوصول إلى ملفات الوسائط على الأجهزة التي تعمل بنظام التشغيل Android 9 (المستوى 28 من واجهة برمجة التطبيقات) أقل، وأعلِن عن READ_EXTERNAL_STORAGE وضبط maxSdkVersion على 28.

يشرح هذا الدليل حالات الاستخدام المختلفة التي يدعمها إطار العمل. التعامل مع الملفات والمستندات الأخرى. كما يشرح كيفية إجراء العمليات في الموقع الذي يختاره المستخدم.

حالات الاستخدام للوصول إلى المستندات والملفات الأخرى

يتوافق "إطار عمل الوصول إلى مساحة التخزين" مع حالات الاستخدام التالية للوصول إلى والملفات والمستندات الأخرى.

إنشاء ملف جديد
ACTION_CREATE_DOCUMENT إجراء intent للمستخدمين بحفظ ملف في موقع محدد
فتح مستند أو ملف
ACTION_OPEN_DOCUMENT إجراء intent يتيح للمستخدمين اختيار مستند أو ملف معيّن لفتحه.
منح إمكانية الوصول إلى محتوى الدليل
ACTION_OPEN_DOCUMENT_TREE إنّ ميزة الإجراء intent، الذي يتوفر على Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يسمح للمستخدمين واختيار دليل محدد، ومنح التطبيق إمكانية الوصول إلى جميع الملفات أو الأدلة الفرعية ضمن هذا الدليل.

تقدِّم الأقسام التالية إرشادات حول كيفية ضبط كل حالة استخدام.

إنشاء ملف جديد

يمكنك استخدام ACTION_CREATE_DOCUMENT إجراء intent لتحميل منتقي ملفات النظام والسماح للمستخدم باختيار مكان كتابة محتويات الملف. تشبه هذه العملية الذي تم استخدامه في القائمة "حفظ باسم" لمربعات الحوار التي تستخدمها أنظمة التشغيل الأخرى.

ملاحظة: لا يمكن لـ 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 intent، والذي يفتح تطبيق منتقي الملفات في النظام. لإظهار أنواع التي يدعمها تطبيقك، حدد نوع 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 لواجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام ACTION_OPEN_DOCUMENT إجراء intent لمطالبة المستخدم باختيار فرد من الأدلة التالية:

  • دليل Android/data/ وجميع الأدلة الفرعية.
  • دليل Android/obb/ وجميع الأدلة الفرعية.

منح الإذن بالوصول إلى محتوى دليل

عادةً ما تدير تطبيقات إدارة الملفات وإنشاء الوسائط مجموعات من الملفات في التسلسل الهرمي للدليل. لتوفير هذه الإمكانية في تطبيقك، يمكنك استخدام ACTION_OPEN_DOCUMENT_TREE إجراء intent، الذي يسمح للمستخدم بمنح الإذن بالوصول إلى دليل كامل شجرة مع بعض الاستثناءات بدءًا من 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 لواجهة برمجة التطبيقات) والإصدارات الأحدث، لا يمكنك استخدام إجراء واحد (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) الخاص بهذا الملف، ويبقى صالحًا إلى أن يتم منح المستخدم إذن الوصول إلى عنوان URL تتم إعادة تشغيله. ومع ذلك، لنفترض أن تطبيقك عبارة عن تطبيق لتعديل الصور، وتريد تمكين المستخدمين من الوصول مباشرةً إلى الصور الخمس التي عدّلوها مؤخرًا من تطبيقك. في حال إعادة تشغيل جهاز المستخدم، عليك إرسال طلب إلى المستخدم. إلى منتقي النظام للعثور على الملفات.

للحفاظ على إمكانية الوصول إلى الملفات على جميع الأجهزة، تتم إعادة تشغيل الجهاز وإنشاء مستخدم أفضل. المستخدم، يمكن لتطبيقك "أخذ" إذن عنوان 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

إدخال مصدر البيانات

يعرض مقتطف الرمز التالي كيفية فتح كائن enterStream نظرًا معرّف الموارد المنتظم (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();
}

تعديل مستند

يمكنك استخدام "إطار عمل الوصول إلى مساحة التخزين" لتعديل مستند نصي.

يستبدل مقتطف الرمز التالي محتوى المستند المعروض. باستخدام عنوان 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() مع معرّفات الموارد المنتظِمة ExternalStorageProvider. مشغَّلة في Android 12 (المستوى 31 لواجهة برمجة التطبيقات) والإصدارات الأحدث، تتيح هذه الطريقة أيضًا MediaDocumentsProvider معرّف موارد منتظم (URI)

فتح ملف افتراضي

في الإصدار Android 7.0 (المستوى 25 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يمكن لتطبيقك الاستفادة من الملفات الافتراضية. الذي يتيحه "إطار عمل الوصول إلى مساحة التخزين" على الرغم من أن الملفات الافتراضية تمثيلاً ثنائيًا، فيمكن للتطبيق فتح محتوياته عن طريق إجبار إلى نوع ملف مختلف أو من خلال عرضها باستخدام ACTION_VIEW هدف اتخاذ القرار.

لفتح الملفات الافتراضية، يجب أن يتضمّن تطبيق العميل منطقًا خاصًا لمعالجة معهم. إذا كنت ترغب في الحصول على تمثيل بالبايت للملف - لمعاينة الملف، على سبيل المثال - يجب طلب نوع 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();
}

مصادر إضافية

لمزيد من المعلومات عن كيفية تخزين المستندات والملفات الأخرى والوصول إليها، يُرجى الرجوع إلى الموارد التالية.

نماذج

الفيديوهات