Veröffentlicht: 20. Februar 2025
Das zuverlässige Herunterladen großer KI-Modelle ist eine Herausforderung. Wenn die Internetverbindung der Nutzer unterbrochen wird oder sie Ihre Website oder Webanwendung schließen, gehen teilweise heruntergeladene Modelldateien verloren und sie müssen bei der Rückkehr auf Ihre Seite von vorn beginnen. Wenn Sie die Background Fetch API als progressive Verbesserung verwenden, können Sie die Nutzerfreundlichkeit erheblich verbessern.
Service Worker registrieren
Für die Background Fetch API muss Ihre App einen Dienst-Worker registrieren.
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
const registration = await navigator.serviceWorker.register('sw.js');
console.log('Service worker registered for scope', registration.scope);
});
}
Hintergrundabruf auslösen
Während des Abrufs wird dem Nutzer der Fortschritt angezeigt und er kann den Download abbrechen. Sobald der Download abgeschlossen ist, startet der Browser den Dienst-Worker und die Anwendung kann mit der Antwort Aktionen ausführen.
Die Background Fetch API kann den Abruf sogar so vorbereiten, dass er gestartet wird, während Sie offline sind. Sobald die Verbindung wiederhergestellt ist, beginnt der Download. Wenn der Nutzer offline geht, wird der Vorgang pausiert, bis er wieder online ist.
Im folgenden Beispiel klickt der Nutzer auf eine Schaltfläche, um Gemma 2B herunterzuladen. Bevor wir das Modell abrufen, prüfen wir, ob es bereits heruntergeladen und im Cache gespeichert wurde, damit keine unnötigen Ressourcen verbraucht werden. Wenn es nicht im Cache vorhanden ist, starten wir den Hintergrundabruf.
const FETCH_ID = 'gemma-2b';
const MODEL_URL =
'https://storage.googleapis.com/jmstore/kaggleweb/grader/g-2b-it-gpu-int4.bin';
downloadButton.addEventListener('click', async (event) => {
// If the model is already downloaded, return it from the cache.
const modelAlreadyDownloaded = await caches.match(MODEL_URL);
if (modelAlreadyDownloaded) {
const modelBlob = await modelAlreadyDownloaded.blob();
// Do something with the model.
console.log(modelBlob);
return;
}
// The model still needs to be downloaded.
// Feature detection and fallback to classic `fetch()`.
if (!('BackgroundFetchManager' in self)) {
try {
const response = await fetch(MODEL_URL);
if (!response.ok || response.status !== 200) {
throw new Error(`Download failed ${MODEL_URL}`);
}
const modelBlob = await response.blob();
// Do something with the model.
console.log(modelBlob);
return;
} catch (err) {
console.error(err);
}
}
// The service worker registration.
const registration = await navigator.serviceWorker.ready;
// Check if there's already a background fetch running for the `FETCH_ID`.
let bgFetch = await registration.backgroundFetch.get(FETCH_ID);
// If not, start a background fetch.
if (!bgFetch) {
bgFetch = await registration.backgroundFetch.fetch(FETCH_ID, MODEL_URL, {
title: 'Gemma 2B model',
icons: [
{
src: 'icon.png',
size: '128x128',
type: 'image/png',
},
],
downloadTotal: await getResourceSize(MODEL_URL),
});
}
});
Die Funktion getResourceSize()
gibt die Byte-Größe des Downloads zurück. Du kannst das mit einer HEAD
-Anfrage implementieren.
const getResourceSize = async (url) => {
try {
const response = await fetch(url, { method: 'HEAD' });
if (response.ok) {
return response.headers.get('Content-Length');
}
console.error(`HTTP error: ${response.status}`);
return 0;
} catch (error) {
console.error('Error fetching content size:', error);
return 0;
}
};
Downloadfortschritt des Berichts
Sobald der Hintergrundabruf beginnt, gibt der Browser eine BackgroundFetchRegistration
zurück.
Mit dem Ereignis progress
können Sie den Nutzer über den Downloadfortschritt informieren.
bgFetch.addEventListener('progress', (e) => {
// There's no download progress yet.
if (!bgFetch.downloadTotal) {
return;
}
// Something went wrong.
if (bgFetch.failureReason) {
console.error(bgFetch.failureReason);
}
if (bgFetch.result === 'success') {
return;
}
// Update the user about progress.
console.log(`${bgFetch.downloaded} / ${bgFetch.downloadTotal}`);
});
Nutzer und Client über den Abschluss des Abrufs benachrichtigen
Wenn der Abruf im Hintergrund erfolgreich war, erhält der Service Worker Ihrer App ein backgroundfetchsuccess
-Ereignis.
Der folgende Code ist im Service Worker enthalten. Mit dem updateUI()
-Aufruf am Ende kannst du die Benutzeroberfläche des Browsers aktualisieren, um den Nutzer über den erfolgreichen Abruf im Hintergrund zu informieren. Informieren Sie den Kunden abschließend über den abgeschlossenen Download, z. B. mit postMessage()
.
self.addEventListener('backgroundfetchsuccess', (event) => {
// Get the background fetch registration.
const bgFetch = event.registration;
event.waitUntil(
(async () => {
// Open a cache named 'downloads'.
const cache = await caches.open('downloads');
// Go over all records in the background fetch registration.
// (In the running example, there's just one record, but this way
// the code is future-proof.)
const records = await bgFetch.matchAll();
// Wait for the response(s) to be ready, then cache it/them.
const promises = records.map(async (record) => {
const response = await record.responseReady;
await cache.put(record.request, response);
});
await Promise.all(promises);
// Update the browser UI.
event.updateUI({ title: 'Model downloaded' });
// Inform the clients that the model was downloaded.
self.clients.matchAll().then((clientList) => {
for (const client of clientList) {
client.postMessage({
message: 'download-complete',
id: bgFetch.id,
});
}
});
})(),
);
});
Nachrichten vom Dienst-Worker empfangen
Wenn du die gesendete Erfolgsmeldung zum abgeschlossenen Download auf dem Client erhalten möchtest, musst du auf message
-Ereignisse warten. Sobald Sie die Nachricht vom Service Worker erhalten haben, können Sie mit dem KI-Modell arbeiten und es mit der Cache API speichern.
navigator.serviceWorker.addEventListener('message', async (event) => {
const cache = await caches.open('downloads');
const keys = await cache.keys();
for (const key of keys) {
const modelBlob = await cache
.match(key)
.then((response) => response.blob());
// Do something with the model.
console.log(modelBlob);
}
});
Abruf im Hintergrund abbrechen
Wenn der Nutzer einen laufenden Download abbrechen können soll, verwende die Methode abort()
der BackgroundFetchRegistration
.
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
return;
}
await bgFetch.abort();
Modell im Cache speichern
Heruntergeladene Modelle im Cache speichern, damit Nutzer das Modell nur einmal herunterladen müssen. Die Background Fetch API verbessert zwar den Downloadvorgang, Sie sollten jedoch immer das kleinstmögliche Modell für die clientseitige KI verwenden.
Zusammengenommen helfen Ihnen diese APIs, die KI-Funktionen für Ihre Nutzer zu verbessern.
Demo
Eine vollständige Implementierung dieses Ansatzes finden Sie in der Demo und im Quellcode.

Danksagungen
Dieser Leitfaden wurde von François Beaufort, Andre Bandarra, Sebastian Benz, Maud Nalpas und Alexandra Klepper geprüft.