เมื่อแอปต้องการเข้าถึงไฟล์ที่แอปอื่นแชร์ แอปที่ขอ (ไคลเอ็นต์) มักจะส่งคำขอไปยังแอปที่แชร์ไฟล์ (เซิร์ฟเวอร์) ในกรณีส่วนใหญ่ คำขอจะเริ่มต้น 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 นี้ไม่ได้ชี้ไปยังสิ่งใดก็ตามที่อยู่นอกไดเรกทอรีที่กำหนด โดยตรวจสอบว่าไม่มีการพยายามทำการข้ามผ่านเส้นทาง เฉพาะแอปไคลเอ็นต์เท่านั้นที่ควรมีสิทธิ์เข้าถึงไฟล์ และต้องเป็นสิทธิ์ที่แอปเซิร์ฟเวอร์ให้เท่านั้น สิทธิ์ดังกล่าวเป็นแบบชั่วคราว ดังนั้นเมื่อสแต็กงานของแอปไคลเอ็นต์เสร็จสิ้นแล้ว ไฟล์จะเข้าถึงได้จากแอปเซิร์ฟเวอร์เท่านั้น
ข้อมูลโค้ดต่อไปนี้แสดงวิธีที่แอปไคลเอ็นต์จัดการ 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
ซึ่งจะใช้เพื่ออ่านไฟล์ได้
ดูข้อมูลที่เกี่ยวข้องเพิ่มเติมได้ที่