Запрос общего файла

Когда приложение хочет получить доступ к файлу, совместно используемому другим приложением, запрашивающее приложение (клиент) обычно отправляет запрос приложению, совместно использующему файлы (серверу). В большинстве случаев запрос запускает Activity в серверном приложении, которое отображает файлы, которыми оно может поделиться. Пользователь выбирает файл, после чего серверное приложение возвращает URI содержимого файла клиентскому приложению.

В этом уроке показано, как клиентское приложение запрашивает файл у серверного приложения, получает URI содержимого файла от серверного приложения и открывает файл, используя URI содержимого.

Отправить запрос на файл

Чтобы запросить файл из серверного приложения, клиентское приложение вызывает startActivityForResult с Intent содержащим такое действие, как ACTION_PICK , и тип MIME, который может обрабатывать клиентское приложение.

Например, следующий фрагмент кода демонстрирует, как отправить Intent серверному приложению, чтобы запустить Activity , описанное в разделе «Общий доступ к файлу» :

Котлин

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 . Это Intent передается клиентскому приложению при переопределении onActivityResult() . Как только клиентское приложение получит URI содержимого файла, оно сможет получить доступ к файлу, получив его FileDescriptor .

Безопасность файлов сохраняется в этом процессе только до тех пор, пока вы правильно анализируете URI контента, который получает клиентское приложение. При анализе содержимого вы должны убедиться, что этот URI не указывает ни на что за пределами предполагаемого каталога, гарантируя, что не будет предпринято никаких попыток обхода пути . Только клиентское приложение должно иметь доступ к файлу и только с разрешениями, предоставленными серверным приложением. Разрешения являются временными, поэтому после завершения стека задач клиентского приложения файл больше не будет доступен за пределами серверного приложения.

Следующий фрагмент демонстрирует, как клиентское приложение обрабатывает Intent , отправленное из серверного приложения, и как клиентское приложение получает FileDescriptor используя URI контента:

Котлин

/*
 * 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 , который затем можно использовать для чтения файла.

Для получения дополнительной информации см.: