공유 파일 요청

앱이 다른 앱에서 공유한 파일에 액세스하려 하면 일반적으로 요청 앱(클라이언트)은 파일을 공유하는 앱(서버)에 요청을 전송합니다. 대부분의 경우 요청은 공유할 수 있는 파일을 표시하는 서버 앱에서 Activity를 시작합니다. 사용자가 파일을 선택하고 나면 서버 앱에서는 파일의 콘텐츠 URI를 클라이언트 앱에 반환합니다.

이 강의에서는 클라이언트 앱이 서버 앱의 파일을 요청하고 서버 앱에서 파일의 콘텐츠 URI를 수신한 후 콘텐츠 URI를 사용하여 파일을 여는 방법을 안내합니다.

파일 요청 전송

서버 앱의 파일을 요청하기 위해 클라이언트 앱은 ACTION_PICK과 같은 작업과 클라이언트 앱에서 처리할 수 있는 MIME 유형이 포함된 IntentstartActivityForResult를 호출합니다.

예를 들어 다음 코드 스니펫에서는 파일 공유에 설명된 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)
            ...
        }
        ...
    }
    

자바

    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의 클라이언트 앱으로 되돌려 보냅니다. 이 IntentonActivityResult()의 재정의에서 클라이언트 앱으로 전달됩니다. 파일의 콘텐츠 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
            ...
        }
    }
    

자바

        /*
         * 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 개체를 이 개체에서 가져옵니다.

추가 관련 정보는 다음을 참조하세요.