當應用程式想要存取其他應用程式共用的檔案時,要求應用程式 (用戶端) 通常會向共用檔案的應用程式 (伺服器) 傳送要求。在大多數情況下,這項要求會在伺服器應用程式中啟動 Activity
,顯示可供共用的檔案。使用者選取檔案,然後伺服器應用程式就會將檔案的內容 URI 傳回用戶端應用程式。
本課程將說明用戶端應用程式如何從伺服器應用程式要求檔案、從伺服器應用程式接收檔案的內容 URI,並使用內容 URI 開啟檔案。
傳送檔案要求
如要從伺服器應用程式要求檔案,用戶端應用程式會使用 Intent
呼叫 startActivityForResult
,其中包含 ACTION_PICK
以及用戶端應用程式可處理的動作 (例如 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); ... } ... }
存取要求的檔案
伺服器應用程式會在 Intent
中將檔案的內容 URI 傳回用戶端應用程式。這個 Intent
會在 onActivityResult()
的覆寫值中傳遞至用戶端應用程式。用戶端應用程式取得檔案的內容 URI 後,即可取得檔案的 FileDescriptor
來存取檔案。
只要您正確解析用戶端應用程式收到的內容 URI,就能在這個程序中保留檔案安全性。剖析內容時,請務必確保此 URI 不會指向預期目錄以外的任何項目,以免嘗試路徑穿越。只有用戶端應用程式應取得檔案存取權,且僅限用戶端應用程式授予的權限。權限是暫時性的,因此一旦用戶端應用程式的作業堆疊完成,就無法再從伺服器應用程式以外的地方存取檔案。
下一段程式碼片段示範用戶端應用程式如何處理從伺服器應用程式傳送的 Intent
,以及用戶端應用程式如何使用內容 URI 取得 FileDescriptor
:
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
物件,供該物件用於讀取檔案。
如需其他相關資訊,請參閱: