Como solicitar um arquivo compartilhado

Quando um app quer acessar um arquivo compartilhado por outro app, o app solicitante (o cliente) geralmente envia uma solicitação para o app que compartilha os arquivos (o servidor). Na maioria dos casos, a solicitação inicia um Activity no app do servidor que exibe os arquivos que ele pode compartilhar. O usuário escolhe um arquivo. Depois, o app do servidor retorna o URI de conteúdo do arquivo para o app cliente.

Esta lição mostra como um app cliente solicita um arquivo de um app do servidor, recebe o URI de conteúdo do arquivo do app do servidor e abre o arquivo usando o URI de conteúdo.

Enviar uma solicitação de arquivo

Para solicitar um arquivo do app do servidor, o app cliente chama startActivityForResult com um Intent contendo a ação, como ACTION_PICK, e um tipo MIME que o app cliente pode processar.

Por exemplo, o snippet de código a seguir demonstra como enviar um Intent a um app de servidor para iniciar o Activity descrito em Como compartilhar um arquivo:

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);
        ...
    }
    ...
}

Acessar o arquivo solicitado

O app do servidor envia o URI de conteúdo do arquivo de volta ao app cliente em um Intent. Esse Intent é transmitido para o app cliente na substituição de onActivityResult(). Depois que o app cliente tem o URI de conteúdo do arquivo, ele pode acessar o arquivo, recebendo o FileDescriptor.

A segurança de arquivos é preservada nesse processo apenas quando você analisa corretamente o URI de conteúdo recebido pelo app cliente. Ao analisar conteúdo, você precisa garantir que esse URI não aponte para nada fora do diretório pretendido, garantindo que nenhuma travessia de caminho esteja sendo tentada. Somente o app cliente terá acesso ao arquivo e somente para as permissões concedidas pelo app do servidor. As permissões são temporárias. Portanto, quando a pilha de tarefas do app cliente for concluída, o arquivo não poderá mais ser acessado fora do app do servidor.

O próximo snippet demonstra como o app cliente processa a Intent enviada do app do servidor e como o app cliente recebe o FileDescriptor usando o URI de conteúdo:

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();
            ...
        }
    }

O método openFileDescriptor() retorna um ParcelFileDescriptor para o arquivo. A partir desse objeto, o app cliente recebe um objeto FileDescriptor, que pode ser usado para ler o arquivo.

Para ver mais informações relacionadas, consulte: