เมื่อแอปต้องการเข้าถึงไฟล์ที่แชร์โดยแอปอื่น แอปที่ส่งคำขอ (ไคลเอ็นต์)
มักจะส่งคำขอไปยังแอปที่แชร์ไฟล์ (เซิร์ฟเวอร์) ในกรณีส่วนใหญ่ คำขอ
เริ่มใช้ Activity
ในแอปเซิร์ฟเวอร์ที่แสดงไฟล์ที่แชร์ได้
ผู้ใช้เลือกไฟล์ หลังจากนั้นแอปเซิร์ฟเวอร์จะส่ง URI เนื้อหาของไฟล์ไปยัง
แอปไคลเอ็นต์
บทเรียนนี้แสดงวิธีที่แอปไคลเอ็นต์ร้องขอไฟล์จากแอปเซิร์ฟเวอร์และรับ URI เนื้อหาจากแอปเซิร์ฟเวอร์ และเปิดไฟล์โดยใช้ URI เนื้อหา
ส่งคำขอไฟล์
หากต้องการขอไฟล์จากแอปเซิร์ฟเวอร์ แอปไคลเอ็นต์จะเรียก
startActivityForResult
ที่มี
Intent
ที่มีการดำเนินการ เช่น
ACTION_PICK
และประเภท MIME ที่แอปไคลเอ็นต์
สามารถจัดการได้
ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้แสดงวิธีส่ง
Intent
ไปยังแอปของเซิร์ฟเวอร์เพื่อเริ่ม
Activity
อธิบายไว้ในการแชร์ไฟล์:
Kotlin
class MainActivity : Activity() { private lateinit var requestFileIntent: Intent private lateinit var inputPFD: ParcelFileDescriptor ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) requestFileIntent = Intent(Intent.ACTION_PICK).apply { type = "image/jpg" } ... } ... private fun requestFile() { /** * When the user requests a file, send an Intent to the * server app. * files. */ startActivityForResult(requestFileIntent, 0) ... } ... }
Java
public class MainActivity extends Activity { private Intent requestFileIntent; private ParcelFileDescriptor inputPFD; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestFileIntent = new Intent(Intent.ACTION_PICK); requestFileIntent.setType("image/jpg"); ... } ... protected void requestFile() { /** * When the user requests a file, send an Intent to the * server app. * files. */ startActivityForResult(requestFileIntent, 0); ... } ... }
เข้าถึงไฟล์ที่ขอ
แอปเซิร์ฟเวอร์จะส่ง URI เนื้อหาของไฟล์กลับไปยังแอปไคลเอ็นต์ใน
Intent
มีการส่ง Intent
นี้ไปยังไคลเอ็นต์
ในการลบล้าง onActivityResult()
ครั้งเดียว
แอปไคลเอ็นต์มี URI เนื้อหาของไฟล์ โดยจะเข้าถึงไฟล์ได้โดยรับ
FileDescriptor
กระบวนการนี้จะรักษาความปลอดภัยของไฟล์ไว้ตราบเท่าที่คุณแยกวิเคราะห์ URI เนื้อหาได้อย่างถูกต้องเท่านั้น ที่แอปไคลเอ็นต์ได้รับ เมื่อแยกวิเคราะห์เนื้อหา คุณต้องตรวจสอบว่า URI นี้ไม่ได้ชี้ไป สิ่งที่อยู่นอกไดเรกทอรีที่กำหนด โดยไม่ตรวจสอบ มีการพยายามทำ path Traversal มีเพียงแอปไคลเอ็นต์เท่านั้นที่ควรได้รับสิทธิ์เข้าถึงไฟล์ และเฉพาะสิทธิ์ที่ได้รับจาก แอปเซิร์ฟเวอร์ สิทธิ์เป็นแบบชั่วคราว ดังนั้นเมื่อสแต็กงานของแอปไคลเอ็นต์เสร็จสิ้น ไม่สามารถเข้าถึงไฟล์ภายนอกแอปเซิร์ฟเวอร์ได้อีกต่อไป
ข้อมูลโค้ดถัดไปจะแสดงวิธีที่แอปไคลเอ็นต์จัดการกับ
Intent
ที่ส่งจากแอปเซิร์ฟเวอร์และวิธีที่แอปไคลเอ็นต์ได้รับ
FileDescriptor
โดยใช้ URI เนื้อหา:
Kotlin
/* * When the Activity of the app that hosts files sets a result and calls * finish(), this method is invoked. The returned Intent contains the * content URI of a selected file. The result code indicates if the * selection worked or not. */ public override fun onActivityResult(requestCode: Int, resultCode: Int, returnIntent: Intent) { // If the selection didn't work if (resultCode != Activity.RESULT_OK) { // Exit without doing anything else return } // Get the file's content URI from the incoming Intent returnIntent.data?.also { returnUri -> /* * Try to open the file for "read" access using the * returned URI. If the file isn't found, write to the * error log and return. */ inputPFD = try { /* * Get the content resolver instance for this context, and use it * to get a ParcelFileDescriptor for the file. */ contentResolver.openFileDescriptor(returnUri, "r") } catch (e: FileNotFoundException) { e.printStackTrace() Log.e("MainActivity", "File not found.") return } // Get a regular file descriptor for the file val fd = inputPFD.fileDescriptor ... } }
Java
/* * When the Activity of the app that hosts files sets a result and calls * finish(), this method is invoked. The returned Intent contains the * content URI of a selected file. The result code indicates if the * selection worked or not. */ @Override public void onActivityResult(int requestCode, int resultCode, Intent returnIntent) { // If the selection didn't work if (resultCode != RESULT_OK) { // Exit without doing anything else return; } else { // Get the file's content URI from the incoming Intent Uri returnUri = returnIntent.getData(); /* * Try to open the file for "read" access using the * returned URI. If the file isn't found, write to the * error log and return. */ try { /* * Get the content resolver instance for this context, and use it * to get a ParcelFileDescriptor for the file. */ inputPFD = getContentResolver().openFileDescriptor(returnUri, "r"); } catch (FileNotFoundException e) { e.printStackTrace(); Log.e("MainActivity", "File not found."); return; } // Get a regular file descriptor for the file FileDescriptor fd = inputPFD.getFileDescriptor(); ... } }
เมธอด openFileDescriptor()
แสดงผล ParcelFileDescriptor
ของไฟล์ ไคลเอ็นต์จากออบเจ็กต์นี้
แอปจะได้รับออบเจ็กต์ FileDescriptor
ซึ่งจะใช้อ่านไฟล์ได้
ดูข้อมูลที่เกี่ยวข้องเพิ่มเติมได้ที่