2017-08-03 6 views
0

Ich habe ein Benutzer-Image-Steuerelement, ein Identicon - wenn der Benutzer ein Bild hat, sollte es angezeigt werden, wenn nicht, zeigt es ihre Initialen mit einem farbigen Hintergrund (und einige andere HTML).Erhalten Sie ein Bild mit HTML-Fallback mit Caching?

Es gibt eine große Anzahl dieser Bilder, wobei viele gleichzeitig angezeigt werden und derselbe Benutzer möglicherweise mehrmals auf einer Seite geladen wird. Bilder können Alpha-Folien enthalten und gegen mehrere unterschiedliche Hintergrundfarben angezeigt werden. Bilder können High-DPI sein und der Fallback (und die meisten der anderen Bilder, die sie nebenan anzeigen) verwenden SVG.

Bilder können sich ändern, aber nicht oft. In der Regel können sie für ein paar Tage oder Wochen zwischengespeichert werden.

Ich denke, es gibt zwei Ansätze dazu:

  • JavaScript
    In diesem Modell wird der Server JSON zurückgibt, die ich in IndexedDB Cache und die JSON enthält einen Daten-URI für das Bild, wenn gesetzt. Zusätzliches JS wird benötigt, um zu prüfen, ob eine Anfrage bereits für denselben Benutzer erstellt wurde, und es zu vermeiden, sie zu wiederholen.

  • HTML <object> Fallback
    Dafür gibt der Server die tatsächlichen Bildbytes zurück, oder ein 404, wenn nicht gefunden. Dann, wenn das Bild nicht der Inhalt des <object> Tag gefunden wird, macht:

#identicon, 
 
#identicon > div { display: inline-block; width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: red; overflow: hidden; }
<object id="identicon" data="api/users/joebloggs/photo" type="image/png"> 
 
    <div>JB</div> 
 
    <!-- some more HTML and SVG content, snipped here --> 
 
</object> 
 

 
<object id="identicon" data="http://placehold.it/50" type="image/png"> 
 
    <div>PH</div> 
 
    <!-- some more HTML and SVG content, snipped here --> 
 
</object>

Es gibt erhebliche Nachteile für die JS-Option, wie auch wenn das JS stark IndexedDB optimiert bedeutet Callbacks (oder synchrone Optionen, wie LocalStorage, neigen dazu, langsam zu sein). Dies beinhaltet ziemlich viel JS für was sollte eine ziemlich einfache Aufgabe sein. Es hat einen großen Vorteil - es fragt nur einmal jedes Benutzerfoto.

Ich mag die Einfachheit der <object> Fallback, hat es einige kleinere Nachteile (viele seltsame Regeln gelten für, wie die HTML in einem Objekt gerendert wird), aber ein großes Problem: wenn das Bild nicht gefunden wird es Rundreisen der Server jedes Mal wieder. Viele Benutzer haben keine Bilder. Jede Seitenladung erhält einen langen Stapel von 404s für Benutzerbilder, von denen sie wissen sollte, dass sie keine haben.

  1. JS ist der beste Weg, bleib dabei - egal wie man JS/Overhead macht?
  2. Lassen Sie die <object> mit den 404s.
  3. ein Ereignis an die <object> hinzufügen, dass einige JS feuert, wenn er das Bild nicht geladen werden kann, aber ich bin nicht sicher, wie es wieder versuchen, zu stoppen, ohne Rückgriff auf (1)
  4. ändern Sie die Serverantwort einen Benutzer zurück Bild auch wenn es fehlschlägt.
    Dies verliert die Eleganz der Fallback und bedeutet viele Server-generierte Bilder.
    Dies ist, was SO/Gravatar tut, aber einige Probleme für uns mit High-DPI-Bildschirmen bedeutet.
  5. Ändern Sie die Serverantwort, um bei einem Fehler ein transparentes Bild zurückzugeben.
    Dies bedeutet, dass das gleiche Bild viele Male im Cache gespeichert wird und bedeutet, dass der Inhalt selbst für Benutzer, die ein Foto haben, überlagert wird. Es verursacht auch Probleme für Bilder mit Alpha-Folien.
  6. Passen Sie den Service Worker an, um Anforderungen für Benutzerabbilder als Sonderfall zu behandeln. 404 Antworten werden zwischengespeichert und die Netzwerkumgehung wird übersprungen, wenn sie erneut versucht werden.
  7. Missbrauch einige andere HTTP-Statusantwort, die <object> noch sieht als ein Fehler, das Bild zu finden und fällt auf den Inhalt zurück, aber das wird vom Browser zwischengespeichert.
  8. Ein anderer Weg?

Alle diese beinhalten Kompromisse, aber ich bin auch ziemlich sicher, dass ich neu zu erfinden das Rad hier - die Chancen jemand sind (in den alle Identicon Implementierungen, die bereits da draußen sind) dies bereits gelöst hat. Kann mir jemand in die richtige Richtung weisen oder den besten Weg vorschlagen, das Problem zu lösen?

+0

Ich bin mir nicht sicher, ob ich alles bekommen habe, aber warum lässt du die Browser nicht selbst mit ihrem Cache umgehen? Anstatt fette b64-Daten Ihrer Bilder zu senden, senden Sie einfach cache-fähige URLs aus Ihrer API in den JSON. Nur dann würden Sie Ihre Bilder mit empfangenen URLs füllen (sei es ein Standardbild, das von Ihrem Benutzer, oder einfach eine Flagge, die Sie wissen lässt, dass Sie es generieren müssen) – Kaiido

+0

@Kaiido Das ist möglich - ich muss noch speichern/rufen Sie diese URL jedoch von einer lokalen Quelle ab. Ich muss noch überprüfen, ob Joe Bloggs ein Foto hat und Jane Bloggs nicht von der 'IndexedDB', und dann warte auf den Rückruf, und dann geh das Bild holen. Durch den Wechsel von Daten-URI zu einer URL wird die Größe dieser ursprünglichen Anforderung reduziert, die Gesamtgröße im Cache wird jedoch nicht reduziert und eine Netzwerkanforderung wird hinzugefügt, wenn jeder Benutzer das erste Mal aufgerufen wird. – Keith

+0

Sie können dann wahrscheinlich alle URIs im synchronen lokalen Speicher anpassen. Und ja, es wird auch den Cache reduzieren: B64-Encoding erhöht die Größe Ihrer Daten um ~ 30%. – Kaiido

Antwort

0

Sie können einen cache-control Header mit einem 404 verwenden und der Browser es zu ehren, und diese Prüfung ist viel schneller als ein Aufruf an localStorage oder IndexedDB.

Die Lösung ist nur die Caching-Header der 404-Antwort hinzuzufügen. In .NET Core ist dies:

context.Response.StatusCode = (int)HttpStatusCode.NotFound; 
context.Response.Headers.Add("Cache-Control", $"public,max-age={duration}"); 

Dann wird es nicht an den Server zurückgehen, bis die Dauer abgelaufen ist, obwohl kein Bild gefunden wurde, <object> fällt immer noch auf seinen Inhalt hin und <img> feuert noch eine error Veranstaltung.