앱이 다른 앱에서 공유한 파일에 액세스하려고 하면 일반적으로 요청 앱 (클라이언트)은 파일을 공유하는 앱 (서버)에 요청을 보냅니다. 대부분의 경우 요청은 공유할 수 있는 파일을 표시하는 서버 앱에서 Activity
를 시작합니다.
사용자가 파일을 선택하면 서버 앱이 파일의 콘텐츠 URI를 클라이언트 앱에 반환합니다.
이 과정에서는 클라이언트 앱이 서버 앱의 파일을 요청하고 서버 앱에서 파일의 콘텐츠 URI를 수신한 후 콘텐츠 URI를 사용하여 파일을 여는 방법을 보여줍니다.
파일 요청 전송
서버 앱에서 파일을 요청하기 위해 클라이언트 앱은 ACTION_PICK
와 같은 작업과 클라이언트 앱에서 처리할 수 있는 MIME 유형이 포함된 Intent
를 사용하여 startActivityForResult
를 호출합니다.
예를 들어 다음 코드 스니펫은 파일 공유에 설명된 Activity
를 시작하기 위해 서버 앱에 Intent
를 전송하는 방법을 보여줍니다.
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
를 처리하는 방법과 클라이언트 앱이 콘텐츠 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
객체를 가져오고 이 객체를 사용하여 파일을 읽을 수 있습니다.
추가 관련 정보는 다음을 참조하세요.