دسترسی به اسناد و فایل‌های دیگر از فضای ذخیره‌سازی مشترک

در دستگاه‌هایی که Android نسخه 4.4 (سطح API 19) و بالاتر دارند، برنامه شما می‌تواند با ارائه‌دهنده اسناد ، از جمله حجم‌های حافظه خارجی و فضای ذخیره‌سازی مبتنی بر ابر، با استفاده از چارچوب دسترسی به فضای ذخیره‌سازی، تعامل داشته باشد. این چارچوب به کاربران اجازه می‌دهد تا با انتخابگر سیستم برای انتخاب ارائه‌دهنده اسناد و انتخاب اسناد خاص و فایل‌های دیگر برای ایجاد، باز کردن یا اصلاح برنامه‌تان، تعامل داشته باشند.

از آنجایی که کاربر در انتخاب فایل‌ها یا دایرکتوری‌هایی که برنامه شما می‌تواند به آنها دسترسی داشته باشد، دخالت دارد، این مکانیسم به هیچ مجوزی از سیستم نیاز ندارد و کنترل کاربر و حریم خصوصی افزایش می‌یابد. به‌علاوه، این فایل‌ها که خارج از دایرکتوری مخصوص برنامه و خارج از فروشگاه رسانه ذخیره می‌شوند، پس از حذف نصب برنامه در دستگاه باقی می‌مانند.

استفاده از چارچوب شامل مراحل زیر است:

  1. یک برنامه قصدی را فراخوانی می کند که حاوی یک عمل مربوط به فضای ذخیره سازی است. این عمل مربوط به یک مورد استفاده خاص است که چارچوب در دسترس قرار می دهد.
  2. کاربر یک انتخابگر سیستم را می‌بیند، که به او امکان می‌دهد یک ارائه‌دهنده اسناد را مرور کند و مکان یا سندی را انتخاب کند که در آن عملیات مربوط به ذخیره‌سازی انجام می‌شود.
  3. این برنامه به یک URI دسترسی خواندن و نوشتن به دست می آورد که نشان دهنده مکان یا سند انتخابی کاربر است. با استفاده از این URI، برنامه می تواند عملیات را در مکان انتخابی انجام دهد .

برای پشتیبانی از دسترسی به فایل های رسانه ای در دستگاه هایی که دارای Android 9 (سطح API 28) یا پایین تر هستند، مجوز READ_EXTERNAL_STORAGE را اعلام کرده و maxSdkVersion روی 28 تنظیم کنید.

این راهنما موارد استفاده متفاوتی را که چارچوب برای کار با فایل‌ها و سایر اسناد پشتیبانی می‌کند، توضیح می‌دهد. همچنین نحوه انجام عملیات در مکان انتخاب شده توسط کاربر را توضیح می دهد.

از موارد برای دسترسی به اسناد و فایل های دیگر استفاده کنید

Storage Access Framework از موارد استفاده زیر برای دسترسی به فایل ها و سایر اسناد پشتیبانی می کند.

یک فایل جدید ایجاد کنید
عمل قصد ACTION_CREATE_DOCUMENT به کاربران امکان می دهد فایلی را در یک مکان خاص ذخیره کنند.
یک سند یا فایل را باز کنید
عمل قصد ACTION_OPEN_DOCUMENT به کاربران امکان می دهد سند یا فایل خاصی را برای باز کردن انتخاب کنند.
اجازه دسترسی به محتویات یک دایرکتوری
کنش هدف ACTION_OPEN_DOCUMENT_TREE که در Android نسخه 5.0 (سطح API 21) و بالاتر موجود است، به کاربران اجازه می‌دهد تا فهرستی خاص را انتخاب کنند و به برنامه شما اجازه دسترسی به همه فایل‌ها و زیرمجموعه‌های موجود در آن فهرست را می‌دهد.

بخش های زیر راهنمایی در مورد نحوه پیکربندی هر مورد استفاده ارائه می دهد.

یک فایل جدید ایجاد کنید

از عمل قصد ACTION_CREATE_DOCUMENT برای بارگیری انتخابگر فایل سیستم استفاده کنید و به کاربر اجازه دهید مکانی را برای نوشتن محتویات یک فایل انتخاب کند. این فرآیند مشابه فرآیندی است که در دیالوگ های "ذخیره به عنوان" استفاده می شود که سایر سیستم عامل ها از آن استفاده می کنند.

توجه: ACTION_CREATE_DOCUMENT نمی تواند یک فایل موجود را بازنویسی کند. اگر برنامه شما سعی می کند فایلی با همین نام ذخیره کند، سیستم یک عدد در پرانتز در انتهای نام فایل اضافه می کند.

برای مثال، اگر برنامه شما سعی کند فایلی به نام confirmation.pdf را در فهرستی که قبلاً فایلی با آن نام دارد ذخیره کند، سیستم فایل جدید را با نام confirmation(1).pdf ذخیره می کند.

هنگام پیکربندی intent، نام فایل و نوع MIME را مشخص کنید، و به صورت اختیاری URI فایل یا دایرکتوری را که انتخابگر فایل هنگام بارگیری برای اولین بار با استفاده از هدف اضافی EXTRA_INITIAL_URI نمایش دهد، مشخص کنید.

قطعه کد زیر نحوه ایجاد و فراخوانی intent برای ایجاد یک فایل را نشان می دهد:

کاتلین

// 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)
}

جاوا

// 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 را نشان می دهد:

کاتلین

// 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)
}

جاوا

// 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 (سطح API 30) و بالاتر، نمی‌توانید از اقدام قصد ACTION_OPEN_DOCUMENT برای درخواست از کاربر درخواست انتخاب فایل‌های فردی از فهرست‌های زیر استفاده کنید:

  • فهرست Android/data/ و همه زیر شاخه ها.
  • دایرکتوری Android/obb/ و همه زیر شاخه ها.

اجازه دسترسی به محتویات یک دایرکتوری

برنامه های مدیریت فایل و ایجاد رسانه معمولاً گروه هایی از فایل ها را در یک سلسله مراتب دایرکتوری مدیریت می کنند. برای ارائه این قابلیت در برنامه خود، از اقدام قصد ACTION_OPEN_DOCUMENT_TREE استفاده کنید، که به کاربر اجازه می دهد تا به کل درخت فهرست راهنمای دسترسی داشته باشد، به استثنای برخی استثناها که از Android 11 شروع می شود (سطح API 30). سپس برنامه شما می تواند به هر فایل موجود در فهرست انتخابی و هر یک از زیر شاخه های آن دسترسی داشته باشد.

هنگام استفاده از ACTION_OPEN_DOCUMENT_TREE ، برنامه شما فقط به فایل‌های فهرستی که کاربر انتخاب می‌کند دسترسی پیدا می‌کند. شما به فایل های برنامه های دیگر که خارج از این فهرست انتخاب شده توسط کاربر قرار دارند، دسترسی ندارید. این دسترسی کنترل شده توسط کاربر به کاربران این امکان را می دهد که دقیقاً چه محتوایی را با برنامه شما به اشتراک بگذارند انتخاب کنند.

به صورت اختیاری، می‌توانید با استفاده از هدف اضافی EXTRA_INITIAL_URI ، URI دایرکتوری را که انتخابگر فایل باید در هنگام بارگیری برای اولین بار نمایش دهد، مشخص کنید.

قطعه کد زیر نحوه ایجاد و فراخوانی قصد برای باز کردن دایرکتوری را نشان می دهد:

کاتلین

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)
}

جاوا

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 (سطح API 30) و بالاتر، نمی‌توانید از اقدام قصد ACTION_OPEN_DOCUMENT_TREE برای درخواست دسترسی به دایرکتوری‌های زیر استفاده کنید:

  • دایرکتوری ریشه حجم حافظه داخلی.
  • دایرکتوری ریشه هر حجم کارت SD که سازنده دستگاه آن را قابل اعتماد می داند، صرف نظر از اینکه کارت شبیه سازی شده یا قابل جابجایی است. حجم قابل اعتماد حجمی است که یک برنامه در بیشتر مواقع با موفقیت به آن دسترسی داشته باشد.
  • دایرکتوری Download .

علاوه بر این، در Android 11 (سطح API 30) و بالاتر، نمی‌توانید از اقدام قصد ACTION_OPEN_DOCUMENT_TREE برای درخواست از کاربر برای انتخاب فایل‌های فردی از فهرست‌های زیر استفاده کنید:

  • فهرست Android/data/ و همه زیر شاخه ها.
  • دایرکتوری Android/obb/ و همه زیر شاخه ها.

انجام عملیات در محل انتخاب شده

پس از اینکه کاربر یک فایل یا دایرکتوری را با استفاده از انتخابگر فایل سیستم انتخاب کرد، می توانید URI مورد انتخابی را با استفاده از کد زیر در onActivityResult() بازیابی کنید:

کاتلین

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.
        }
    }
}

جاوا

@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 را بررسی کنید. سپس UI برنامه شما می تواند فقط گزینه هایی را که ارائه دهنده پشتیبانی می کند نشان دهد.

ادامه مجوزها

هنگامی که برنامه شما فایلی را برای خواندن یا نوشتن باز می کند، سیستم مجوز URI را برای آن فایل به برنامه شما می دهد، که تا زمانی که دستگاه کاربر راه اندازی مجدد شود ادامه می یابد. با این حال، فرض کنید که برنامه شما یک برنامه ویرایش تصویر است و می خواهید کاربران بتوانند به 5 تصویری که اخیراً ویرایش کرده اند، مستقیماً از برنامه شما دسترسی داشته باشند. اگر دستگاه کاربر راه اندازی مجدد شده است، باید کاربر را به انتخابگر سیستم برگردانید تا فایل ها را پیدا کند.

برای حفظ دسترسی به فایل‌ها در سراسر راه‌اندازی مجدد دستگاه و ایجاد یک تجربه کاربری بهتر، برنامه شما می‌تواند مجوز URI پایداری را که سیستم ارائه می‌دهد، «دریافت» کند، همانطور که در قطعه کد زیر نشان داده شده است:

کاتلین

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)

جاوا

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 را می گیرد و آن را ثبت می کند:

کاتلین

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")
        }
    }
}

جاوا

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 آن نشان می دهد:

کاتلین

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
}

جاوا

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 آن نشان می دهد. در این قطعه، خطوط فایل به صورت یک رشته خوانده می شود:

کاتلین

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()
}

جاوا

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 داده شده را بازنویسی می کند:

کاتلین

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()
    }
}

جاوا

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 سند را دارید.COLUMN_FLAGS حاوی SUPPORTS_DELETE است، می‌توانید سند را حذف کنید. به عنوان مثال:

کاتلین

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri)

جاوا

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri);

یک URI رسانه معادل را بازیابی کنید

متد getMediaUri() یک URI ذخیره رسانه ارائه می کند که معادل URI ارائه دهنده اسناد داده شده است. 2 URI به یک مورد اساسی اشاره دارد. با استفاده از URI فروشگاه رسانه، می‌توانید راحت‌تر به فایل‌های رسانه از فضای ذخیره‌سازی مشترک دسترسی داشته باشید .

متد getMediaUri() از URI های ExternalStorageProvider پشتیبانی می کند. در اندروید 12 (سطح API 31) و بالاتر، این روش از URI های MediaDocumentsProvider نیز پشتیبانی می کند.

یک فایل مجازی باز کنید

در Android نسخه 7.0 (سطح API 25) و بالاتر، برنامه شما می‌تواند از فایل‌های مجازی که چارچوب دسترسی به فضای ذخیره‌سازی در دسترس قرار می‌دهد استفاده کند. حتی اگر فایل‌های مجازی نمایش باینری ندارند، برنامه شما می‌تواند محتویات آن‌ها را با وادار کردن آن‌ها به یک نوع فایل متفاوت یا با مشاهده آن فایل‌ها با استفاده از عمل قصد ACTION_VIEW باز کند.

برای باز کردن فایل‌های مجازی، برنامه مشتری شما باید منطق ویژه‌ای برای مدیریت آن‌ها داشته باشد. اگر می خواهید یک نمایش بایتی از فایل دریافت کنید - برای مثال برای پیش نمایش فایل - باید یک نوع MIME جایگزین را از ارائه دهنده اسناد درخواست کنید.

پس از انتخاب کاربر، از URI در داده‌های نتایج برای تعیین مجازی بودن فایل استفاده کنید، همانطور که در قطعه کد زیر نشان داده شده است:

کاتلین

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
}

جاوا

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" وادار کنید. قطعه کد زیر نشان می دهد که چگونه می توان بررسی کرد که آیا یک فایل مجازی می تواند به عنوان یک تصویر نمایش داده شود یا خیر، و اگر چنین است، یک جریان ورودی از فایل مجازی دریافت می کند:

کاتلین

@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()
    }
}

جاوا

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();
}

منابع اضافی

برای اطلاعات بیشتر در مورد نحوه ذخیره و دسترسی به اسناد و فایل های دیگر، به منابع زیر مراجعه کنید.

نمونه ها

ویدیوها