공유 파일 요청

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

이 과정에서는 클라이언트 앱이 서버 앱에서 파일을 요청하고, 서버 앱에서 파일의 콘텐츠 URI를 수신한 후 콘텐츠 URI를 사용하여 파일을 여는 방법을 보여줍니다.

파일 요청 전송

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

예를 들어 다음 코드 스니펫은 파일 공유에 설명된 Activity를 시작하기 위해 Intent를 서버 앱으로 전송하는 방법을 보여줍니다.

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를 가져오는 방법을 보여줍니다.

/*
 * 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 객체를 가져오며, 이 객체는 나중에 파일을 읽는 데 사용할 수 있습니다.

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