Speichern im Browser

Permalink

Damit mein HTML5 Podcatcher weiß welche Podcasts er abspielen soll muss man ihm die Adressen der jeweiligen RSS-Feeds mitteilen. Dies geschieht über ein kleines Formular mit einem Eingabefeld und einem Button. Diese Adresse kann man dann im Internet abrufen und aus den im Feed enthaltenen Episoden eine Playlist zusammenstellen. Wie das funktioniert habe ich ja schon im vorangegangenen Artikel gezeigt.

Ohne weitere Maßnahmen würde der Podcatcher aber ständig vergessen welche Podcasts abonniert sind. Daher braucht es eine Möglichkeit solche Informationen abzuspeichern.

Web Storage

Web Storage ist eine vom W3C spezifizierte API um einen sogenannten Key-Value-Speicher mit Javascript zu nutzen. Einfach ausgedrückt kann der Browser damit beliebige Werte unter einem individuell festgelegten Schlüsselwort speichern. Damit es hierbei nicht zu heillosem Durcheinander kommt wird ein solcher Speicher vom Browser für jede Domain gesondert gemanagt.

Diese API nutze ich nun um die abonnierten Feeds abzuspeichern. Dazu habe ich den Zugriff auf den Web Storage durch drei Methoden gekapselt:

var readSource = function(sourceUri) {
  "use strict";
  var source;
  source = JSON.parse(
    localStorage.getItem('source.' + sourceUri)
  );
  if (!source) {
    source = { 'uri': sourceUri };
  }
  return source;
};
var writeSource = function(source) {
  "use strict";  
  localStorage.setItem('source.' + source.uri, JSON.stringify(source));
};
var deleteSource = function(source) {
  "use strict";
  localStorage.removeItem('source.' + source.uri);
};

Als Schlüssel verwende ich die URL des Feeds in Kombination mit einem Prefix. Der Prefix hilft später die Feeds von den Episoden und sonstigen Konfigurationswerten auseinanderzuhalten. Die URL ist praktischerweise ein eindeutiger Wert. So muss ich nicht mit Konflikten rechnen.

Da es reichlich mühsam wäre jeden Text, jede Zahl und jedes Datum einzeln zu speichern und zu laden fasse ich alle relevanten Daten eines Feeds in einem Javascript-Objekt zusammen und speichere dieses. Das ist möglich da ich die Objekte in einen JSON-String umwandelte. Das laden funktioniert entsprechend umgekehrt.

Eine weitere Eigenschaft der Web-Storage-API hilft dabei die Gesamtliste aller bereits gespeicherter Feeds zu erzeugen:

var readSourceList = function() {
  "use strict";
  var i, sourcelist = [];
  for (i = 0; i < localStorage.length; i++) {
    if (localStorage.key(i).slice(0, 7) === 'source.') {
      sourcelist.push(
        readSource(
          localStorage.key(i).substring(7)
        )
      );
    }
  }
  return sourcelist;
};

Hierbei wird die Liste aller Im Speicher vorhandenen Schlüssel durchlaufen um dabei die Feeds herauszufischen.

Mit den Funktionen die mir jetzt zur Verfügung stehen kann ich die Verwaltung der Podcasts-Feeds schnell auf die Beine stellen. Zwei Eventhandler erledigen die Arbeit. Einer fügt Feeds über das oben erwähnte Formular zum Web Storage hinzu und einer löscht sie wieder raus:

$('#addSourceButton').on('click', function(event) {
  event.preventDefault();
  event.stopPropagation();
  var parserresult, entryUI, i;
  parserresult = downloadSource(
    readSource($('#addSourceUrlInput').val())
  );
  writeSource(parserresult.source);
});
$('#sources').on('click', '.deleteSource', function(event) {
  event.preventDefault();
  event.stopPropagation();
  var source, i;
  source = readSource(
    $(this).closest('li').data('sourceuri')
  );
  deleteSource(source);
});

Das Storage-Event

Das Storage-Event ist Bestandteil der Web Storage API und soll immer dann auftreten wenn etwas im Storage gespeichert oder gelöscht wird. Ich wollte damit eine zentrale Routine etablieren die alle Änderungen an den Feeds in die Benutzeroberfläche überträgt, also das HTML-Markup ergänzt und aktualisiert.

Leider funktioniert das nicht so wie ich es mir gedacht habe. Die Events werden zwar ausgelöst, allerdings nur in den Tabs und offenen Fenstern die nicht selbst die ursprüngliche Aktion durchgeführt haben. Die Seite die das Javascript ausführt das den Web Storage verändert kann also nichts mit dem Event anfangen da sie es nicht erhält.

Für meine Zwecke ist das Storage-Event damit zwar nutzlos, kann aber genutzt werden um zwischen geöffneten Dokumenten der selben Domain zu kommunizieren. Ein kleines Beispiel hierzu gibt es bei HTML5 Demos. Ich glaube aber für diesen Anwendungsfall gibt es bessere Lösungen.

Ausblick

Das Vorgehen das ich hier aufgezeigt habe funktioniert wunderbar um alle anfallenden Daten zu den abonnierten Podcast-Feeds zu speichern. Außerdem lässt sich dieselbe Technik ebenso nutzen um Daten zu den einzelnen Episoden zu speichern. Dadurch ist sichergestellt das ein Großteil der anfallenden Daten gespeichert werden kann und damit auch dauerhaft und offline zur Verfügung steht. Das alle verbreiteten Browser (auch die mobilen) die API unterstützen sind auch diesbezüglich keine Einschränkungen in Sicht.

Für Binärdaten und andere größere Happen ist die Web Storage API allerdings nicht geeignet. Das liegt unter anderem an Größenbeschränkungen der Browser und andererseits daran das die einzelnen Speicherstellen nicht über eine URL adressierbar sind. Für MP3-Dateien muss ich mir also noch etwas anderes einfallen lassen.

Wer das oben gesagte einfach nicht glauben kann darf gerne wieder selber ausprobieren und sich überzeugen.