เข้าถึงเอกสารและไฟล์อื่นๆ จากพื้นที่เก็บข้อมูลที่ใช้ร่วมกัน

ในอุปกรณ์ที่ใช้ Android 4.4 (API ระดับ 19) ขึ้นไป แอปของคุณสามารถโต้ตอบ กับผู้ให้บริการเอกสาร เช่น ปริมาณพื้นที่เก็บข้อมูลภายนอกและพื้นที่เก็บข้อมูลระบบคลาวด์ โดยใช้พื้นที่เก็บข้อมูล เฟรมเวิร์กการเข้าถึง เฟรมเวิร์กนี้ช่วยให้ผู้ใช้โต้ตอบกับเครื่องมือเลือกของระบบได้ เพื่อเลือกผู้ให้บริการเอกสาร และเลือกเอกสารที่ต้องการ รวมถึงไฟล์อื่นๆ สำหรับ เพื่อสร้าง เปิด หรือแก้ไขแอปของคุณ

เนื่องจากผู้ใช้มีส่วนเกี่ยวข้องในการเลือกไฟล์หรือไดเรกทอรีที่แอปของคุณ สามารถเข้าถึง กลไกนี้ไม่ต้องใช้ระบบใดๆ สิทธิ์ รวมถึงการควบคุมและความเป็นส่วนตัวของผู้ใช้ ได้รับการปรับปรุงแล้ว นอกจากนี้ ไฟล์เหล่านี้ซึ่งจัดเก็บไว้นอกไฟล์ ไดเรกทอรีเฉพาะแอปและนอก Media Store จะยังคงอยู่ในอุปกรณ์ หลังจากถอนการติดตั้งแอปแล้ว

การใช้เฟรมเวิร์กนี้มีขั้นตอนดังต่อไปนี้

  1. แอปเรียกใช้ Intent ที่มีการทำงานที่เกี่ยวข้องกับพื้นที่เก็บข้อมูล การดำเนินการนี้ สอดคล้องกับ Use Case ที่เจาะจงที่เฟรมเวิร์ก พร้อมใช้งาน
  2. ผู้ใช้จะเห็นเครื่องมือเลือกของระบบ ซึ่งอนุญาตให้เรียกดูผู้ให้บริการเอกสารได้ และเลือกตำแหน่งหรือเอกสารที่มีการดำเนินการเกี่ยวกับพื้นที่เก็บข้อมูล
  3. แอปจะได้รับสิทธิ์อ่านและเขียนใน URI ที่แสดงถึง สถานที่หรือเอกสารที่เลือกไว้ การใช้ URI นี้ ทำให้แอปสามารถดำเนินการต่างๆ สถานที่ที่เลือก

เพื่อรองรับการเข้าถึงไฟล์สื่อในอุปกรณ์ที่ใช้ Android 9 (API ระดับ 28) หรือ ให้ประกาศ READ_EXTERNAL_STORAGE และตั้งค่า maxSdkVersion เป็น 28

คู่มือนี้อธิบายกรณีการใช้งานต่างๆ ที่เฟรมเวิร์กรองรับ การทำงานกับไฟล์และเอกสารอื่นๆ ทั้งยังอธิบายวิธีดำเนินการ ในตำแหน่งที่ตั้งที่ผู้ใช้เลือก

กรณีการใช้งานสำหรับการเข้าถึงเอกสารและไฟล์อื่นๆ

เฟรมเวิร์กการเข้าถึงพื้นที่เก็บข้อมูลรองรับกรณีการใช้งานต่อไปนี้สำหรับการเข้าถึง ไฟล์และเอกสารอื่นๆ

สร้างไฟล์ใหม่
ACTION_CREATE_DOCUMENT การดำเนินการผ่าน Intent ช่วยให้ผู้ใช้บันทึกไฟล์ในตำแหน่งที่เจาะจงได้
เปิดเอกสารหรือไฟล์
ACTION_OPEN_DOCUMENT การดำเนินการผ่าน Intent ช่วยให้ผู้ใช้เลือกเอกสารหรือไฟล์ที่ต้องการเปิดได้
ให้สิทธิ์เข้าถึงเนื้อหาของไดเรกทอรี
ACTION_OPEN_DOCUMENT_TREE การดำเนินการผ่าน Intent ที่พร้อมใช้งานใน Android 5.0 (API ระดับ 21) ขึ้นไปช่วยให้ผู้ใช้ เลือกไดเรกทอรีที่เจาะจง โดยให้สิทธิ์แอปของคุณในการเข้าถึงไฟล์ทั้งหมด ไดเรกทอรีย่อยภายในไดเรกทอรีนั้น

ส่วนต่อไปนี้จะให้คําแนะนําเกี่ยวกับวิธีกําหนดค่ากรณีการใช้งานแต่ละกรณี

สร้างไฟล์ใหม่

ใช้เมนู ACTION_CREATE_DOCUMENT Intent เพื่อโหลดเครื่องมือเลือกไฟล์ระบบ และอนุญาตให้ผู้ใช้เลือก ตำแหน่งที่จะเขียนเนื้อหาของไฟล์ กระบวนการนี้คล้ายกับ ใช้ในการ "บันทึกเป็น" ที่ระบบปฏิบัติการอื่นใช้

หมายเหตุ: ACTION_CREATE_DOCUMENT ไม่สามารถเขียนทับ ไฟล์ที่มีอยู่ ถ้าแอปของคุณพยายามบันทึกไฟล์ที่มีชื่อเดียวกัน ระบบจะ ใส่ตัวเลขในวงเล็บต่อท้ายชื่อไฟล์

ตัวอย่างเช่น หากแอปพยายามบันทึกไฟล์ที่ชื่อว่า confirmation.pdf ในไดเรกทอรีที่มีไฟล์ที่มีไฟล์ดังกล่าวแล้ว ระบบจะบันทึกไฟล์ใหม่ด้วยชื่อ confirmation(1).pdf

เมื่อกำหนดค่า Intent ให้ระบุชื่อไฟล์และประเภท MIME และ (ไม่บังคับ) ระบุ URI ของไฟล์หรือไดเรกทอรีที่เครื่องมือเลือกไฟล์ควร แสดงเมื่อโหลดเป็นครั้งแรกโดยใช้ EXTRA_INITIAL_URI Intent เพิ่มเติม

ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้างและเรียกใช้ Intent กำลังสร้างไฟล์:

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 Intent เพิ่มเติม

ข้อมูลโค้ดต่อไปนี้แสดงวิธีการสร้างและเรียกใช้ 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);
}

การจำกัดการเข้าถึง

ใน Android 11 (API ระดับ 30) ขึ้นไป คุณจะใช้ ACTION_OPEN_DOCUMENT การดำเนินการผ่าน Intent ที่จะขอให้ผู้ใช้เลือก จากไดเรกทอรีต่อไปนี้

  • ไดเรกทอรี Android/data/ และไดเรกทอรีย่อยทั้งหมด
  • ไดเรกทอรี Android/obb/ และไดเรกทอรีย่อยทั้งหมด

ให้สิทธิ์เข้าถึงเนื้อหาของไดเรกทอรี

โดยทั่วไปแล้ว แอปการจัดการไฟล์และสร้างสื่อจะจัดการกลุ่มไฟล์ใน ลำดับชั้นของไดเรกทอรี หากต้องการใช้ความสามารถนี้ในแอป ให้ใช้ ACTION_OPEN_DOCUMENT_TREE การดำเนินการ Intent ซึ่งอนุญาตให้ผู้ใช้ให้สิทธิ์เข้าถึงไดเรกทอรีทั้งหมด แผนผัง โดยมีข้อยกเว้นบางอย่างเริ่มตั้งแต่ Android 11 (API ระดับ 30) แอปของคุณสามารถ จากนั้นให้เข้าถึงไฟล์ในไดเรกทอรีที่เลือก และไดเรกทอรีย่อยใดๆ ของไดเรกทอรีนั้น

เมื่อใช้ ACTION_OPEN_DOCUMENT_TREE แอปของคุณจะได้รับสิทธิ์เข้าถึงเฉพาะ ไฟล์ในไดเรกทอรีที่ผู้ใช้เลือก คุณไม่มีสิทธิ์เข้าถึง ของ Google ที่อยู่นอกไดเรกทอรีที่ผู้ใช้เลือกนี้ ช่วงเวลานี้ การเข้าถึงที่ผู้ใช้ควบคุมทำให้ผู้ใช้เลือกเนื้อหาที่ต้องการได้ แชร์กับแอปของคุณได้อย่างสบายใจ

(ไม่บังคับ) คุณสามารถระบุ URI ของไดเรกทอรีที่เครื่องมือเลือกไฟล์ควร แสดงเมื่อโหลดเป็นครั้งแรกโดยใช้ EXTRA_INITIAL_URI Intent เพิ่มเติม

ข้อมูลโค้ดต่อไปนี้แสดงวิธีการสร้างและเรียกใช้ Intent สำหรับการเปิด ไดเรกทอรี:

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 (API ระดับ 30) ขึ้นไป คุณจะใช้ ACTION_OPEN_DOCUMENT_TREE การดำเนินการผ่าน Intent เพื่อขอสิทธิ์เข้าถึงรายการต่อไปนี้ ไดเรกทอรี:

  • ไดเรกทอรีรูทของวอลุ่มที่จัดเก็บข้อมูลภายใน
  • ไดเรกทอรีรากของวอลุ่มการ์ด SD แต่ละอันที่ผู้ผลิตอุปกรณ์ ถือว่าเชื่อถือได้ ไม่ว่าการ์ดจะมีการจำลองหรือ ถอดออกได้ ระดับเสียงที่เชื่อถือได้คือข้อมูลที่แอปเข้าถึงได้เกือบทั้งหมด เวลา
  • ไดเรกทอรี Download

นอกจากนี้ ใน Android 11 (API ระดับ 30) ขึ้นไป คุณยังไม่สามารถใช้ ACTION_OPEN_DOCUMENT_TREE การดำเนินการผ่าน Intent ที่จะขอให้ผู้ใช้เลือก แต่ละไฟล์จากไดเรกทอรีต่อไปนี้

  • ไดเรกทอรี 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 จากนั้น UI ของแอปจะแสดงตัวเลือกที่ผู้ให้บริการรองรับเท่านั้น

รักษาสิทธิ์

เมื่อแอปของคุณเปิดไฟล์เพื่ออ่านหรือเขียน ระบบจะให้ การให้สิทธิ์ 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();
}

แก้ไขเอกสาร

คุณสามารถใช้เฟรมเวิร์กการเข้าถึงพื้นที่เก็บข้อมูลเพื่อแก้ไขเอกสารข้อความที่มีอยู่ได้

ข้อมูลโค้ดต่อไปนี้จะแทนที่เนื้อหาของเอกสารที่แสดง ตาม 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 ทั้ง 2 รายการอ้างถึงรายการพื้นฐานเดียวกัน การใช้สื่อ URI ของสโตร์ คุณสามารถเข้าถึงไฟล์สื่อจาก พื้นที่เก็บข้อมูล

เมธอด getMediaUri() รองรับ URI ExternalStorageProvider เปิด Android 12 (API ระดับ 31) ขึ้นไป เมธอดนี้ยังรองรับ URI MediaDocumentsProvider

เปิดไฟล์เสมือน

ใน Android 7.0 (API ระดับ 25) ขึ้นไป แอปของคุณสามารถใช้ไฟล์เสมือนได้ ที่เฟรมเวิร์กการเข้าถึงพื้นที่เก็บข้อมูลใช้งานได้ แม้ว่าไฟล์เสมือน ไม่มีการนำเสนอแบบไบนารี แอปของคุณสามารถเปิดเนื้อหาได้โดยบังคับ แยกเป็นประเภทไฟล์อื่น หรือด้วยการดูไฟล์เหล่านั้นโดยใช้ Intent 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();
}

แหล่งข้อมูลเพิ่มเติม

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีจัดเก็บและเข้าถึงเอกสารและไฟล์อื่นๆ ให้ดูแหล่งข้อมูลต่อไปนี้

ตัวอย่าง

วิดีโอ