Dateiauswahl

Mit der Dateiauswahl v8 können Sie die gleiche Funktionalität verwenden, die im M365-Dienst in Ihren Lösungen verwendet wird. Das heißt, wenn wir den Dienst weiterentwickeln und verbessern, erscheinen diese neuen Funktionen für Ihre Benutzer!

Dieses neue „Steuerung“ ist eine im Microsoft-Dienst gehostete Seite, mit der Sie über Post-Nachrichten interagieren. Die Seite kann entweder in einen iFrame eingebettet oder als Popup gehostet werden.

Zeigen Sie mir einfach den Beispielcode

Die Dokumentation für die Auswahl v7.2 finden Sie hier.

Erforderliches Setup

Um die Beispiele auszuführen oder die Steuerung in Ihrer Lösung zu verwenden, werden Sie eine AAD-Anwendung erstellen müssen. Sie können die folgenden Schritte ausführen:

  1. Erstellen Sie eine neue AAD-App-Registrierung, und notieren Sie sich die ID der Anwendung.
  2. Erstellen Sie unter „Authentifizierung“ eine neue Einzelseiten-Anwendungsregistrierung.
    1. Legen Sie den Umleitungs-URI auf https://localhost fest (dies dient zum Testen der Beispiele).
    2. Stellen Sie sicher, dass sowohl Zugriffstoken als auch ID-Token aktiviert sind
    3. Sie können diese Anwendung optional mehrmandantenfähig konfigurieren, dies liegt jedoch außerhalb des Bereichs dieses Artikels.
  3. Unter API-Berechtigungen
    1. Fügen Sie Files.Read.All, Sites.Read.All hinzu, belassen Sie User.Read für delegierte Graph-Berechtigungen.
    2. Fügen Sie AllSites.Read, MyFiles.Read für delegierte SharePoint-Berechtigungen hinzu.

Wenn Sie im SharePoint Framework entwickeln, können Sie im Anwendungsmanifest mit der Ressource „SharePoint“ und „Microsoft Graph“ diese Berechtigungen anfordern.

Fordern Sie gegebenenfalls Zugriff auf Files.ReadWrite.All, Sites.ReadWrite.All, AllSites.Write und MyFiles.Write an, um Benutzer*innen das Hochladen von Dateien und das Erstellen von Ordnern in Picker zu erlauben.

Berechtigungen

Die Dateiauswahl arbeitet immer mit delegierten Berechtigungen und kann daher immer nur auf Dateien und Ordner zugreifen, auf die der aktuelle Benutzer bereits Zugriff hat.

Sie müssen mindestens die SharePoint-Berechtigung MyFiles.Read anfordern, um Dateien von den OneDrive- und SharePoint-Websites eines Benutzers zu lesen.

Lesen Sie die folgende Tabelle, um zu verstehen, welche Berechtigungen basierend auf den Vorgängen erforderlich sind, die Sie ausführen möchten. Alle Berechtigungen in dieser Tabelle beziehen sich auf delegierte Berechtigungen.

Lesen Schreiben
OneDrive SharePoint.MyFiles.Read
oder
Graph.Files.Read
SharePoint.MyFiles.Write
oder
Graph.Files.ReadWrite
SharePoint-Websites SharePoint.MyFiles.Read
oder
Graph.Files.Read
oder
SharePoint.AllSites.Read
SharePoint.MyFiles.Write
oder
Graph.Files.ReadWrite
oder
SharePoint.AllSites.Write
Teams-Kanäle Graph.ChannelSettings.Read.All und SharePoint.AllSites.Read Graph.ChannelSettings.Read.All und SharePoint.AllSites.Write

So funktioniert es

Um die Steuerung verwenden zu können, müssen Sie Folgendes ausführen:

  1. Senden Sie eine Post-Anforderung an die Seite „Steuerung“, die unter /_layouts/15/FilePicker.aspx gehostet wird. Mit dieser Anforderung stellen Sie einige Parameter bereit, der wichtigste ist die Auswahlkonfiguration.
  2. Richten Sie das Messaging zwischen Ihrer Hostanwendung und der Steuerung ein, indem Sie postMessage und Nachrichtenports verwenden.
  3. Sobald der Kommunikationskanal eingerichtet wurde, müssen Sie auf verschiedene „Befehle“ reagieren. Der erste besteht darin, Authentifizierungstoken bereitzustellen.
  4. Schließlich müssen Sie auf zusätzliche Befehlsnachrichten reagieren, um neue/unterschiedliche Authentifizierungstoken bereitzustellen, ausgewählte Dateien zu verarbeiten oder das Popup zu schließen.

Die folgenden Abschnitte erklären jeden Schritt.

Außerdem gibt es eine Vielzahl von Beispielen, die verschiedene Möglichkeiten zur Integration in die Steuerung zeigen.

Initiieren der Auswahl

Um die Auswahl zu initiieren, müssen Sie ein „Fenster“ erstellen. Dies kann entweder ein iFrame oder ein Popup sein. Sobald Sie über ein Fenster verfügen, sollten Sie ein Formular erstellen und das Formular per POST an die URL {baseUrl}/_layouts/15/FilePicker.aspx mit den definierten Abfragezeichenfolgenparametern senden.

Der obige Wert {baseUrl} ist entweder die SharePoint-Web-URL des Zielwebs oder das OneDrive des Benutzers. Einige Beispiele sind: "https://tenant.sharepoint.com/sites/dev" oder "https://tenant-my.sharepoint.com".

OneDrive-Consumerkonfiguration

name Beschreibungen
Behörde https://login.microsoftonline.com/consumers
Bereich OneDrive.ReadWrite oder OneDrive.ReadOnly
baseUrl https://onedrive.live.com/picker

Wenn Sie ein Token anfordern, verwenden Sie oder OneDrive.ReadOnlyOneDrive.ReadWrite , wenn Sie das Token anfordern. Wenn Sie die Berechtigungen für Ihre Anwendung anfordern, wählen Sie für Files.Read oder Files.ReadWrite (oder einen anderen Files.X-Bereich) aus.

// 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();

Auswahlkonfiguration

Die Auswahl wird konfiguriert, indem ein JSON-Objekt mit den gewünschten Einstellungen serialisiert und an die Abfragezeichenfolgewerte angefügt wird, wie im Abschnitt Initiieren der Auswahl gezeigt. Sie können auch das gesamte Schema anzeigen. Sie müssen mindestens die Authentifizierungs-, Eingabe- und Messagingeinstellungen bereitstellen.

Unten sehen Sie ein Beispiel für ein Objekt mit minimalen Einstellungen. Dadurch wird das Messaging auf Kanal 27 eingerichtet, die Auswahl wird informiert, dass wir Token bereitstellen können, und dass wir wollen, dass die Registerkarte „Meine Dateien“ die Dateien des OneDrive-Benutzers darstellen soll. Für diese Konfiguration würde eine baseUrl im Format „https://{tenant}-my.sharepoint.com“ verwendet.

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
    },
}

Die Auswahl ist so konzipiert, dass sie sowohl mit OneDrive UND SharePoint in einer bestimmten Instanz funktioniert, und es sollte nur einer der Eintragsabschnitte enthalten sein.

Lokalisierung

Die Benutzeroberfläche des File Picker unterstützt die Lokalisierung für dieselben Sprachen wie SharePoint.

Um die Sprache für File Picker festzulegen, verwenden Sie den Parameter localeQuery String, der auf einen der LCID-Werte in der obigen Liste gesetzt ist.

Einrichten des Messaging

Nachdem das Fenster erstellt und das Formular übermittelt wurde, müssen Sie einen Messagingkanal einrichten. Dieser wird verwendet, um die Befehle von der Auswahl zu empfangen und darauf zu antworten.

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

Implementierung des Nachrichtenlisteners

Ihre Lösung muss verschiedene Nachrichten aus der Auswahl verarbeiten, die entweder als Benachrichtigungen oder Befehle klassifiziert sind. Benachrichtigungen erwarten keine Antwort und können als Protokollinformationen betrachtet werden. Die eine Ausnahme ist die unten hervorgehobene page-loaded-Benachrichtigung, die Sie informiert, dass die Auswahl bereit ist.

Befehle erfordern, dass Sie bestätigen und je nach Befehl antworten. In diesem Abschnitt wird eine Beispielimplementierung der Funktion channelMessageListener angezeigt, die dem Port als Ereignislistener hinzugefügt wurde. In den nächsten Abschnitten werden Benachrichtigungen und Befehle ausführlich erläutert.

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

Token abrufen

Das Steuerelement erfordert, dass wir es mit Authentifizierungstoken basierend auf dem gesendeten Befehl bereitstellen können. Dazu erstellen wir eine Methode, die einen Befehl annimmt und ein Token zurückgibt, wie unten gezeigt. Wir verwenden das @azure/msal-browser Paket, um die Authentifizierung zu verarbeiten.

Derzeit basiert das Steuerelement auf SharePoint-Token und nicht auf Graph. Daher müssen Sie sicherstellen, dass Ihre Ressource korrekt ist und Sie keine Token für Graph-Aufrufe wiederverwenden können.

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

Ergebnisse des ausgewählten Elements

Wenn ein Element ausgewählt wird, gibt die Auswahl über den Messagingkanal ein Array ausgewählter Elemente zurück. Es gibt zwar eine Reihe von Informationen, die zurückgegeben werden können, aber es ist immer garantiert, dass Folgendes enthalten ist:

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

Damit können Sie eine URL erstellen, um eine GET-Anforderung zu erstellen, um alle informationen zu erhalten, die Sie über die ausgewählte Datei benötigen. Es wäre in der Regel von folgendem Format:

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

Sie müssen ein gültiges Token mit den entsprechenden Rechten zum Lesen der Datei in die Anforderung einschließen.

Hochladen von Dateien

Wenn Sie der Anwendung Berechtigungen erteilen Files.ReadWrite.All , die Sie für Auswahltoken verwenden, wird ein Widget im oberen Menü angezeigt, mit dem Sie Dateien und Ordner in die OneDrive- oder SharePoint-Dokumentbibliothek hochladen können. Es sind keine weiteren Konfigurationsänderungen erforderlich. Dieses Verhalten wird von der Anwendung und den Benutzerberechtigungen gesteuert. Beachten Sie, dass die Option nicht angezeigt wird, wenn der Benutzer keinen Zugriff auf den Speicherort zum Hochladen hat.