Gib mir meine Sachen! Authentifizierter Dateidownload in JavaScript


Tue 30 June 2015 Von Josh Fletcher

Vor Kurzem habe ich ein Webinar abgehalten, in dem ich eine eigenständige Webanwendung vorgestellt habe, die CMIS für den Zugriff auf Content eines Nuxeo-Repositorys verwendet hat. Eine Funktion, die mir dabei besonders am Herzen gelegen hat, war das Herunterladen des Ausgabeformats eines Dokuments, das sich auf der Nuxeo Platform befindet. Peinlicherweise habe ich später herausgefunden, dass dieses Beispiel nur dann funktioniert, wenn man bereits in Nuxeo angemeldet ist. Dadurch wird der Sinn, eine eigenständige Webanwendung zu erstellen, jedoch zunichte gemacht!

Also habe ich mich darangemacht, dieses Versehen zu beheben. Wie sich herausgestellt hat, war es schwierig, dafür eine Lösung zu finden. Die Lösung selbst war jedoch sehr einfach. Da das Problem nicht wirklich spezifisch für Nuxeo ist, habe ich mich dazu entschlossen, es in einem eigenen Blog zu behandeln. Diese Informationen sollten für jeden nützlich sein, der Dateien herunterladen muss, die eine Authentifizierung von JavaScript-Code erfordern.

Hinweis: Ich werde über meinen CMIS-Client auch in einem künftigen Beitrag bloggen.

Fallbeispiel


Ich möchte ein Nuxeo-Dokument im PDF-Format über CMIS mithilfe einer einseitigen Webanwendung herunterzuladen. Um es klar zu machen: Die Webanwendung ist keine Nuxeo-Anwendung, sondern nur eine einfache App, die beispielsweise auf Apache ausgeführt wird. Die Anfrage bezieht sich auf eine einfache URL wie die folgende:

http://localhost:8080/nuxeo/json/cmis/default/root?succinct=true&streamId=nuxeo%3Arendition%3Apdf&cmisselector=content&objectId=07cdb579-b845-46a7-b22f-f49fa4f7de8b&download=attachment

Das Problem

Authentifizierung


Natürlich muss die Anfrage authentifiziert werden. Ich möchte generell den unschönen Authentifizierungsdialog des Browsers vermeiden. Das bedeutet, dass die Authentifizierung im Code erfolgen muss. Deshalb reicht ein einfacher <a>-Tag nicht aus. Wenn der Benutzer auf einen Link klickt, navigiert er weg von der einseitigen Anwendung. Deshalb gehen Authentifizierungsinformationen verloren und dem Benutzer wird der Authentifizierungsdialog des Browsers angezeigt. Das gleiche Problem tritt bei Methoden auf, die window.open mit <form>, <iframe> usw. umfassen.

Lokales Speichern der Datei


Des Weiteren möchte ich, dass der Benutzer die Datei wie jede andere Datei herunterladen kann. Vielleicht wird sie im Ordner "Downloads" gespeichert oder sogar im Browser geöffnet, abhängig vom Dateityp und der Browser-Unterstützung.

Die Lösung


Die Lösung besteht aus zwei wesentlichen Teilen:


  • Ein XMLHTTPRequest (XHR)-Aufruf, um die Datei (als Blob) abzurufen; der Aufruf wird authentifiziert.

  • Eine JavaScript-Bibliothek, die den von XHR zurückgegebenen Blob nimmt und im lokalen System als Datei speichert.


Authenticated File Download

Die Erklärung


Um den Blob zu erhalten, müssen Sie nur die Authentifizierung über XHR durchführen. Die integrierten Parameter (username und password) für XHR scheinen nicht zu funktionieren (eventuell sind sie auch gar nicht für diese Verwendung gedacht). Es funktioniert jedoch, den Authorization-Header manuell hinzuzufügen. Zum Beispiel:

xhr.setRequestHeader("Authorization", "Basic " + Base64.encode(userName + ":" + password));

Zum Speichern des Blobs habe ich FileSaver.js verwendet. Soweit ich das entschlüsseln kann, funktioniert es folgendermaßen:


  • Speichern Sie den Blob im temporären Speicher mithilfe der JavaScript FileAPI.

  • Erstellen Sie ein <a>-Element und weisen Sie eine Blob-URL zu, die auf die obige Datei verweist.

  • Aktivieren Sie das HTML5-Download-Attribut für dieses Element.


    • Hinweis: So funktioniert es für Chrome und Firefox, aber es ist browserabhängig. Safari zum Beispiel unterstützt Download noch nicht. FileSaver.js ist robust genug, um mehrere Browser zu unterstützen.



  • "Klicken" Sie über JavaScript auf das Element.


Beachten Sie, dass FileSaver.js die HTML5 W3C saveAs()-Schnittfläche implementiert. Irgendwann werden Browser wahrscheinlich saveAs() nativ implementieren.

Die Ausgangssituation


Die Lösung ist verblüffend einfach. Ich habe mehrere Tage gebraucht, um das Problem zu untersuchen, zu verstehen und zu testen und eine Lösung zu finden. Im Internet habe ich mehrere Möglichkeiten gefunden, "eine Datei mit JavaScript herunterzuladen" und authentifizierte Anfragen mit JavaScript zu stellen. Ich habe jedoch nichts gefunden, bei dem beides auf Seite des Clients kombiniert wird. Bei jeder Lösung, die beides kombinieren wollte, waren Modifikationen auf der Serverseite notwendig.

Warnung: Die Lösung, insbesondere der Datei-Download, ist auf die File API von HTML5 sowie auf das Download-Attribut angewiesen. Der springende Punkt ist, dass es in modernen Browsern gut funktioniert.

Tipps


  • Sie müssen CORS in Nuxeo konfigurieren, um CMIS-Anfragen einer Webanwendung zu erlauben. Ich habe mich dazu entschieden, die Einschränkung nur auf die JSON-Bindung von localhost durchzuführen. Das ist mein Beitrag:

<extension target="org.nuxeo.ecm.platform.web.common.requestcontroller.service.RequestControllerService" point="corsConfig">
<corsConfig name="forCMISBrowserBinding" allowOrigin="http://localhost"&gt;
<pattern>/nuxeo/json/cmis.*</pattern>
</corsConfig>
</extension>


  • download.js scheint eine ältere, jedoch umsetzbare Lösung zu sein. Ich erwähne dies deshalb, weil es scheint, dass das lokale Speichern der Datei übersprungen wird. Sie nimmt den von XHR zurückgegebenen Blob und erstellt einen Anchor-Tag, der direkt im Speicher darauf verweist (unter Verwendung der Blob URL-Syntax). Das Verhalten in Safari ist jedoch viel schlechter als FileSaver.js.


  • Sie können nicht auf den Dateinamen mit XHR zugreifen, sobald Sie CORS verwenden. Hier gibt es eine tolle Erklärung. Zusammenfassung: bei der Verwendung von XHR mit CORS sind Sie auf "einfache Antwortheader" beschränkt. Nuxeo gibt den Dateinamen im content-disposition-Header zurück; auf diesen kann nicht zugegriffen werden. Es ist kein großes Problem, wenn Sie CMIS verwenden, da Sie den Dokumentnamen einfach über das CMIS-Objekt erhalten können.


  • Im Übrigen macht XHRs mit Anmeldeinformationen nichts und scheint nichts mit dem Benutzernamen und dem Passwort, das an XHR weitergegeben wurde, zu tun zu haben. Es wird für die Weitergabe von Cookies mit CORS-Anfragen verwendet. In der Regel schränkt CORS Cookies über Domänen hinweg ein bzw. gibt keine Cookies weiter.

Zu dem Beispiel


Ich habe versucht, das Beispiel so einfach wie möglich zu halten, um Folgendes herauszustellen: a) die Anfrage zu authentifizieren und b) die Datei zu speichern. Es soll nicht als Vorgabe für die beste Methode dienen, sondern eine einfache Erklärung geben, wie es funktioniert.

Sehen Sie sich hier die Beispielanwendung an!


Kategorie: Product & Development
Etikettiert: CMIS, How to, JavaScript