HTML5: Dateien mit Web Storage API & File API speichern

In einem anderen Beitrag hatte ich euch bereits eine kurze Einführung in die HTML5 Web Storage API gegeben. Dabei wurde deutlich, dass als Datentyp für einen zu speichernden Wert nur DOMString unterstützt wird.

Trotzdem ist es möglich auch andere Datentypen, wie z.B. Arrays, Objekte oder aber sogar Dateien mittels der Web Storage API zu speichern. Dazu müssen wir einfach die Datentypen vor dem Speichern in einen String umwandeln.

Wie ihr mithilfe der File API Dateien in einen String umwandelt und via Web Storage API speichert, erfahrt ihr in diesem Beitrag.

Anwendungsbeispiel

In diesem Beispiel wollen wir eine Datei im lokalen Speicher (Web Storage) des Webbrowsers speichern. Anschließend wollen wir die Datei wieder auslesen und anzeigen.

HTML-Grundgerüst

Für unser Beispiel benötigen wir nur ein input-Feld zur Auswahl einer Datei. Zum anschließenden Auslesen und Anzeigen der Datei fügen wir noch eine Schaltfläche und ein iFrame zum Anzeigen des Dateiinhalts ein:

<input id="chooseFile" type="file" name="chooseFile"/>
<input id="showFile" type="button" value="Datei anzeigen"/>
<iframe id="frame" width="500" height="500"/>
HTML

Dateien mittels File & WebStorage API speichern

Damit wir eine Datei im Web Storage des Browsers speichern können, müssen wir die Datei bzw. den Dateiinhalt in einen String umwandeln. Dazu nutzen wir die File API und im konkreten Fall die FileReader-Klasse. Mithilfe dieser Klasse lesen wir den Inhalt der Datei als Data-URL, also als base64-kodierten String, ein:

document.addEventListener("DOMContentLoaded", function ()
{
    document.getElementById('chooseFile').addEventListener('change', function ()
    {
        saveFile(this.files[0]);
    });
});

function saveFile(file)
{
    var fileReader = new FileReader();

    fileReader.onload = function (event)
    {
        localStorage.setItem('myFile', event.target.result);
    };

    fileReader.readAsDataURL(file);
}
JavaScript

Kurze Erläuterung des Codes:

Zeile 1 – 7
Sobald das DOM der Webseite fertig geladen ist, wird ein Event-Handler für das change-Event des chooseFile-Elements erstellt.

Zeile 11
Eine neue Instanz der FileReader-Klasse erstellen.

Zeile 13
Da die FileReader-Klasse asynchron arbeitet, implementieren wir den onload-Event-Handler. Dieser wird aufgerufen sobald die Datei fertig eingelesen wurde.

Zeile 15
Den als Data-URL eingelesenen Dateiinhalt können wir per event.target.result abrufen. Den Data-URL-String speichern wir anschließend mittels Web Storage API und dem localStorage-Objekt.

Zeile 18
Den asynchronen Vorgang zum Einlesen der Datei starten.

Zum Überprüfen, ob die Datei auch wirklich gespeichert wurde, könnt ihr die Entwickler-Konsole eures Webbrowsers verwenden. In der entsprechenden Konsole einfach localStorage eingeben. Es werden euch dann alle gespeicherten Schlüssel-Wert-Paare angezeigt.

Je nach eurer gespeicherten Datei, sieht der Eintrag dann etwa so aus:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAqkAAAEqCAMAAAABNDkeAAAC/VBMVEUDAwIfCRkTEBEPERoNESUHFEk[...]

Datei auslesen und anzeigen

Nun nützt uns eine gespeicherte Datei nicht viel, wenn wir sie nicht auch wieder auslesen können. Dazu erstellen wir uns als erstes einen weiteren Event-Handler für unsere showFile-Schaltfläche:

document.addEventListener("DOMContentLoaded", function ()
{
    document.getElementById('chooseFile').addEventListener('change', function ()
    {
        saveFile(this.files[0]);
    });

    document.getElementById('showFile').addEventListener('click', function ()
    {
        showFile();
    });
});
JavaScript

Als nächstes implementieren wir die showFile-Funktion:

function showFile()
{
    var fileAsDataUrl = localStorage.getItem('myFile'),
        fileData = fileAsDataUrl.split(','),
        contentType,
        uInt8Array,
        fileAsBlob;

    fileAsDataUrl = window.atob(fileData[1]);
    contentType = (fileData[0].split(':')[1]).split(';')[0];

    uInt8Array = new Uint8Array(fileAsDataUrl.length);

    for (var i = 0; i < fileAsDataUrl.length; ++i)
    {
        uInt8Array[i] = fileAsDataUrl.charCodeAt(i);
    }

    fileAsBlob = new Blob([uInt8Array], {'type': contentType});

    document.getElementById('frame').src = window.URL.createObjectURL(fileAsBlob);
}
JavaScript

Dazu wieder eine kurze Erläuterung:

Zeile 3
Auslesen des gespeicherten Dateiinhalts.

Zeile 4
Wir "splitten" den Data-URL-String in zwei Teile und zwar in den Content-Type und eigentlichen Inhalt der Datei.

Zeile 9
Mittels der atob-Funktion dekodieren wir den base64-kodierten Dateiinhalt.

Zeile 10
Der Stringteil der den Content-Type enthält, muss noch ein wenig angepasst werden. Im Moment ist der String nämlich wie folgt aufgebaut: data:contentType;base64. Aus diesem Grund extrahieren wir den Mittelteil, um wirklich den Content-Type zu erhalten.

Zeile 12
Erstellen einer neuen Instanz der Uint8Array-Klasse.

Zeile 14 - 17
Füllen des speziellen Uint8Array-Objekts.

Zeile 19
Den Dateiinhalt in den Datentyp Blob umwandeln. Dazu erstellen wir eine neue Blob-Instanz und übergeben das Uint8Array-Objekt sowie den Content-Type.

Zeile 21
Mittels der window.URL.createObjectURL-Funktion genrieren wir eine spezielle Objekt-URL, die unseren Dateiinhalt referenziert. Die generierte Objekt-URL setzen wir als src des iFrames. Nach einem Klick auf die "Datei anzeigen"-Schaltfläche wird so der Dateiinhalt unserer gespeicherten Datei im iFrame angezeigt.

Webbrowser-Unterstützung

Unterstützt wird die File API in allen modernen Webbrowsern: siehe caniuse.com

Fazit

Unabhängig davon, dass eigentlich nur Werte vom Typ DOMString mittels der Web Storage API gespeichert werden können, lassen sich auch Dateien nach einer einfachen Umwandlung via File API speichern.

Letztendlich ergibt es aber aufgrund des geringen Speicherlimits von ca. 5MB natürlich nur bedingt Sinn Dateien im Web Storage des Webbrowsers zu speichern.

Aktualisierungshistorie:
  • 10. Mai 2013
    ursprüngliche Veröffentlichung in meinem ehemaligen Blog "Smart-Webentwicklung"
Feedback

Für Feedback zum Beitrag, seien es Fragen, Korrigierungen und/oder Anregungen, könnt ihr mir gerne eine Nachricht per E-Mail oder Mastodon schreiben (siehe Kontakt).