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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • ไดเรกทอรีรูทของวอลุ่มที่จัดเก็บข้อมูลภายใน
  • ไดเรกทอรีรากของวอลุ่มการ์ด 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) ขึ้นไป เมธอดนี้จะรองรับ MediaDocumentsProvider URI ด้วย

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

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

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

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

ตัวอย่าง

วิดีโอ