Compartilhar via


Seletor de Arquivos

O Seletor de Arquivos v8 permite que você use a mesma funcionalidade usada no serviço M365 em suas soluções. Ou seja, à medida que iteramos e aprimoramos o serviço, esses novos recursos aparecem para seus usuários!

Esse novo "controle" é uma página hospedada no serviço Microsoft com a qual você interage por meio de mensagens de postagem. A página pode ser hospedada inserida em um iframe ou como pop-up.

Mostrar-me o código de exemplo

Você pode encontrar a documentação do seletor 7.2 aqui.

Configuração necessária

Para executar os exemplos ou usar o controle em sua solução, você precisará criar um aplicativo do AAD. Você pode seguir estas etapas:

  1. Crie um novo Registro de Aplicativo do AAD e anote a ID do aplicativo
  2. Em autenticação, crie um registro de aplicativo de página única
    1. Defina o URI de redirecionamento como https://localhost (isso é para testar os exemplos)
    2. Verifique se os tokens de acesso e os tokens de ID estão marcados
    3. Opcionalmente, você pode configurar este aplicativo para multilocatário, mas isso está fora do escopo deste artigo
  3. Em permissões de API
    1. AdicionarFiles.Read.All, Sites.Read.All, deixar User.Read para permissões delegadas do Graph
    2. Adicionar AllSites.Read, MyFiles.Read para permissões delegadas do SharePoint

Se você estiver desenvolvendo na Estrutura do SharePoint poderá solicitar essas permissões no manifesto do aplicativo com os recursos "SharePoint" e "Microsoft Graph".

Para permitir que o usuário carregue arquivos e crie pastas na experiência do Picker, você pode solicitar acesso a Files.ReadWrite.All, Sites.ReadWrite.All, AllSites.Write, e MyFiles.Write.

Permissões

O seletor de ficheiros funciona sempre com permissões delegadas e, como tal, só pode aceder a ficheiros e pastas às quais o utilizador atual já tem acesso.

No mínimo, tem de pedir ao SharePoint MyFiles.permissão de Ler para ler ficheiros a partir dos sites do OneDrive e do SharePoint de um utilizador.

Reveja a tabela abaixo para compreender que permissão é necessária com base nas operações que pretende efetuar. Todas as permissões nesta tabela referem-se a permissões delegadas.

Ler Gravar
OneDrive SharePoint.MyFiles.Read
ou
Graph.Files.Read
SharePoint.MyFiles.Write
ou
Graph.Files.ReadWrite
SharePoint Sites SharePoint.MyFiles.Read
ou
Graph.Files.Read
ou
SharePoint.AllSites.Read
SharePoint.MyFiles.Write
ou
Graph.Files.ReadWrite
ou
SharePoint.AllSites.Write
Canais do Teams Graph.ChannelSettings.Read.All e SharePoint.AllSites.Read Graph.ChannelSettings.Read.All e SharePoint.AllSites.Write

Como funciona

Para usar o controle, você deve:

  1. Faça uma solicitação POST para a página "controle" hospedada em /_layouts/15/FilePicker.aspx. Ao usar essa solicitação, você fornece alguns parâmetros, o principal é a configuração do seletor.
  2. Configure o sistema de mensagens entre o aplicativo host e o controle usando postMessage e portas de mensagem.
  3. Depois que o canal de comunicação for estabelecido, você deverá responder a vários "comandos", o primeiro deles é fornecer tokens de autenticação.
  4. Por fim, você precisará responder a mensagens de comando adicionais para fornecer tokens de autenticação novos/diferentes, manipular arquivos selecionados ou fechar o pop-up.

As seções a seguir explicam melhor cada cenário.

Também temos uma variedade de exemplos que mostram diferentes maneiras de se integrar ao controle.

Iniciar o Seletor

Para iniciar o seletor, você precisa criar uma "janela", que pode ser um iframe ou um pop-up. Depois de ter uma janela, você deve construir um formulário e fazer POST do formulário para a URL {baseUrl}/_layouts/15/FilePicker.aspx com os parâmetros de cadeia de caracteres de consulta definidos.

O valor {baseUrl} acima é a URL da Web do SharePoint da Web de destino ou o OneDrive do usuário. Alguns exemplos são: "https://tenant.sharepoint.com/sites/dev" ou "https://tenant-my.sharepoint.com".

Configuração do Consumidor do OneDrive

nome descrições
autoridade https://login.microsoftonline.com/consumers
Escopo OneDrive.ReadWrite ou OneDrive.ReadOnly
baseUrl https://onedrive.live.com/picker

Quando pedir um token, irá utilizar o OneDrive.ReadOnly ou OneDrive.ReadWrite quando pedir o token. Quando pedir as permissões para a sua aplicação, irá selecionar para Files.Read ou Files.ReadWrite (ou outro âmbito Ficheiros.X).

// create a new window. The Picker's recommended maximum size is 1080x680, but it can scale down to
// a minimum size of 250x230 for very small screens or very large zoom.
const win = window.open("", "Picker", "width=1080,height=680");

// we need to get an authentication token to use in the form below (more information in auth section)
const authToken = await getToken({
    resource: baseUrl,
    command: "authenticate",
    type: "SharePoint",
});

// to use an iframe you can use code like:
// const frame = document.getElementById("iframe-id");
// const win = frame.contentWindow;

// now we need to construct our query string
// options: These are the picker configuration, see the schema link for a full explaination of the available options
const queryString = new URLSearchParams({
   filePicker: JSON.stringify(options),
   locale: 'en-us'
});

// Use MSAL to get a token for your app, specifying the resource as {baseUrl}.
const accessToken = await getToken(baseUrl);

// we create the absolute url by combining the base url, appending the _layouts path, and including the query string
const url = baseUrl + `/_layouts/15/FilePicker.aspx?${queryString}`);

// create a form
const form = win.document.createElement("form");

// set the action of the form to the url defined above
// This will include the query string options for the picker.
form.setAttribute("action", url);

// must be a post request
form.setAttribute("method", "POST");

// Create a hidden input element to send the OAuth token to the Picker.
// This optional when using a popup window but required when using an iframe.
const tokenInput = win.document.createElement("input");
tokenInput.setAttribute("type", "hidden");
tokenInput.setAttribute("name", "access_token");
tokenInput.setAttribute("value", accessToken);
form.appendChild(tokenInput);

// append the form to the body
win.document.body.append(form);

// submit the form, this will load the picker page
form.submit();

Configuração do Seletor

O seletor é configurado por meio da serialização de um objeto json que contém as configurações desejadas e do acréscimo dele aos valores de querystring, conforme mostrado na seção Iniciar o Seletor. Você também pode exibir o esquema completo. No mínimo, você deve fornecer as configurações de autenticação, entrada e mensagens.

Um exemplo de objeto de configurações mínimas é mostrado abaixo. Isso configura o sistema de mensagens no canal 27, permite que o seletor saiba que podemos fornecer tokens e que queremos que a guia "Meus Arquivos" represente os arquivos do OneDrive do usuário. Essa configuração usaria uma baseUrl do formato "https://{tenant}-my.sharepoint.com";

const channelId = uuid(); // Always use a unique id for the channel when hosting the picker.

const options = {
    sdk: "8.0",
    entry: {
        oneDrive: {}
    },
    // Applications must pass this empty `authentication` option in order to obtain details item data
    // from the picker, or when embedding the picker in an iframe.
    authentication: {},
    messaging: {
        origin: "http://localhost:3000",
        channelId: channelId
    },
}

O seletor foi projetado para funcionar em ambos OneDrive OU SharePoint em uma determinada instância e apenas uma das seções de entrada deve ser incluída.

Localização

A interface do Seletor de Arquivos oferece suporte à localização para o mesmo conjunto de idiomas do SharePoint.

Para definir o idioma do seletor de arquivos, use o parâmetro locale de string de consulta, definido como um dos valores LCID na lista acima.

Estabelecer Mensagens

Depois que a janela for criada e o formulário enviado, você precisará estabelecer um canal de mensagens. Isso é usado para receber os comandos do seletor e responder.

let port: MessagePort;

function initializeMessageListener(event: MessageEvent): void {
    // we validate the message is for us, win here is the same variable as above
    if (event.source && event.source === win) {

        const message = event.data;

        // the channelId is part of the configuration options, but we could have multiple pickers so that is supported via channels
        // On initial load and if it ever refreshes in its window, the Picker will send an 'initialize' message.
        // Communication with the picker should subsequently take place using a `MessageChannel`.
        if (message.type === "initialize" && message.channelId === options.messaging.channelId) {
            // grab the port from the event
            port = event.ports[0];

            // add an event listener to the port (example implementation is in the next section)
            port.addEventListener("message", channelMessageListener);

            // start ("open") the port
            port.start();

            // tell the picker to activate
            port.postMessage({
                type: "activate",
            });
        }
    }
};

// this adds a listener to the current (host) window, which the popup or embed will message when ready
window.addEventListener("message", messageEvent);

Implementação do Ouvinte de Mensagens

Sua solução deve lidar com várias mensagens do seletor, classificadas como notificações ou comandos. As notificações não esperam resposta e podem ser consideradas informações de log. A única exceção é a notificação page-loaded realçada abaixo, que informará que o seletor está pronto.

Os comandos exigem que você reconheça e, dependendo do comando, responda. Esta seção mostra um exemplo de implementação da função channelMessageListener adicionada como um ouvinte de eventos à porta. As próximas seções falam detalhadamente sobre notificações e comandos.

async function channelMessageListener(message: MessageEvent): Promise<void> {
    const payload = message.data;

    switch (payload.type) {

        case "notification":
            const notification = payload.data;

            if (notification.notification === "page-loaded") {
                // here we know that the picker page is loaded and ready for user interaction
            }

            console.log(message.data);
            break;

        case "command":

            // all commands must be acknowledged
            port.postMessage({
                type: "acknowledge",
                id: message.data.id,
            });

            // this is the actual command specific data from the message
            const command = payload.data;

            // command.command is the string name of the command
            switch (command.command) {

                case "authenticate":
                    // the first command to handle is authenticate. This command will be issued any time the picker requires a token
                    // 'getToken' represents a method that can take a command and return a valid auth token for the requested resource
                    try {
                        const token = await getToken(command);

                        if (!token) {
                            throw new Error("Unable to obtain a token.");
                        }

                        // we report a result for the authentication via the previously established port
                        port.postMessage({
                            type: "result",
                            id: message.data.id,
                            data: {
                                result: "token",
                                token: token,
                            }
                        });
                    } catch (error) {
                        port.postMessage({
                            type: "result",
                            id: message.data.id,
                            data: {
                                result: "error",
                                error: {
                                    code: "unableToObtainToken",
                                    message: error.message
                                }
                            }
                        });
                    }

                    break;

                case "close":

                    // in the base of popup this is triggered by a user request to close the window
                    await close(command);

                    break;

                case "pick":

                    try {
                        await pick(command);
    
                        // let the picker know that the pick command was handled (required)
                        port.postMessage({
                            type: "result",
                            id: message.data.id,
                            data: {
                                result: "success"
                            }
                        });
                    } catch (error) {
                        port.postMessage({
                            type: "result",
                            id: message.data.id,
                            data: {
                                result: "error",
                                error: {
                                    code: "unusableItem",
                                    message: error.message
                                }
                            }
                        });
                    }

                    break;

                default:
                    // Always send a reply, if if that reply is that the command is not supported.
                    port.postMessage({
                        type: "result",
                        id: message.data.id,
                        data: {
                            result: "error",
                            error: {
                                code: "unsupportedCommand",
                                message: command.command
                            }
                        }
                    });

                    break;
            }

            break;
    }
}

Obter Token

O controlo requer que possamos fornecer tokens de autenticação com base no comando enviado. Para tal, criamos um método que utiliza um comando e devolve um token, conforme mostrado abaixo. Estamos a utilizar o @azure/msal-browser pacote para processar o trabalho de autenticação.

Atualmente, o controlo depende de tokens do SharePoint e não do Graph, pelo que terá de garantir que o recurso está correto e que não pode reutilizar tokens para chamadas do Graph.

import { PublicClientApplication, Configuration, SilentRequest } from "@azure/msal-browser";
import { combine } from "@pnp/core";
import { IAuthenticateCommand } from "./types";

const app = new PublicClientApplication(msalParams);

async function getToken(command: IAuthenticateCommand): Promise<string> {
    let accessToken = "";
    const authParams = { scopes: [`${combine(command.resource, ".default")}`] };

    try {

        // see if we have already the idtoken saved
        const resp = await app.acquireTokenSilent(authParams!);
        accessToken = resp.accessToken;

    } catch (e) {

        // per examples we fall back to popup
        const resp = await app.loginPopup(authParams!);
        app.setActiveAccount(resp.account);

        if (resp.idToken) {

            const resp2 = await app.acquireTokenSilent(authParams!);
            accessToken = resp2.accessToken;

        } else {

            // throw the error that brought us here
            throw e;
        }
    }

    return accessToken;
}

Resultados do Item Escolhidos

Quando um item é selecionado, o seletor irá devolver, através do canal de mensagens, uma matriz de itens selecionados. Embora exista um conjunto de informações que podem ser devolvidas, é sempre garantido que o seguinte seja incluído:

{
    "id": string,
    "parentReference": {
        "driveId": string
    },
    "@sharePoint.endpoint": string
}

Com isto, pode criar um URL para fazer um pedido GET para obter as informações necessárias sobre o ficheiro selecionado. Geralmente, seria da forma:

@sharePoint.endpoint + /drives/ + parentReference.driveId + /items/ + id

Terá de incluir um token válido com os direitos adequados para ler o ficheiro no pedido.

Carregar Ficheiros

Se conceder Files.ReadWrite.All permissões à aplicação que está a utilizar para tokens de seletor, será apresentado um widget no menu superior, permitindo-lhe carregar ficheiros e pastas para a biblioteca de documentos do OneDrive ou do SharePoint. Não são necessárias outras alterações de configuração. Este comportamento é controlado pelas permissões de aplicação + utilizador. Tenha em atenção que, se o utilizador não tiver acesso à localização a carregar, o seletor não mostrará a opção.

Documentação de Orientação sobre a Imagem Corporativa

As aplicações que se integram com o Selecionador de Ficheiros do Microsoft OneDrive também podem optar por promover a sua integração com o OneDrive para os clientes. Uma vez que o OneDrive tem uma oferta de consumidor e comercial, as seguintes opções estão disponíveis para apresentação em interfaces de aplicações de terceiros:

  • Microsoft OneDrive (pessoal)
    • Também pode ser apresentado como Microsoft OneDrive para pessoal
  • Microsoft OneDrive (trabalho/escola)
    • Também pode ser apresentado como Microsoft OneDrive para trabalho ou escola