Service Worker i cache plików dla transferu offline
Użyj Service Workers, aby włączyć transfer plików offline. Strategie cache, background sync oraz wzorce progressive web app.
CSIRT NASK w swoich rekomendacjach dla aplikacji webowych wskazuje, że utrata danych w trakcie przesyłania pliku wrażliwego to nie tylko problem UX, ale też potencjalny incydent bezpieczeństwa zmuszający użytkownika do ponowienia uploadu przez mniej bezpieczny kanał. Service Workers pozwalają aplikacji do transferu plików działać gdy sieć wysiada: buforuj powłokę HTML i JS przez Cache API, kolejkuj nieudane uploady przez Background Sync API, przechowuj częściowe fragmenty w IndexedDB i odtwarzaj wszystko gdy łączność wróci. Worker działa na osobnym wątku z własną pętlą zdarzeń, przechwytuje zdarzenia fetch dla swojego zakresu i utrzymuje się przez zamknięcia kart. Właściwa recepta dla narzędzi uploadu to strategia stale-while-revalidate dla powłoki aplikacji, kolejkowanie fragmentów oparte na IndexedDB dla trwających transferów i rejestracja Background Sync ponawiająca uploady co 15 minut dopóki nie zakończą się sukcesem.
Co Service Workers faktycznie robią dla aplikacji transferu
Duża wygrana to fakt, że navigator.serviceWorker przeżywa przeładowania kart, okresy offline i nawet uśpienie telefonu. Gdy użytkownik zaczyna upload 2 GB na niestabilnym Wi-Fi pociągowym, chcesz żeby fragmenty, które już zakończyły się sukcesem, pozostały zakończone, te w locie były ponawiał przy ponownym połączeniu i żeby cały stan był odtwarzalny jeśli przeglądarka zabije kartę, żeby odzyskać pamięć. Service Worker — który działa niezależnie od jakiejkolwiek konkretnej karty — jest elementem umożliwiającym to wszystko.
API daje ci trzy bloki budulcowe: Cache do przechowywania odpowiedzi po URL, IndexedDB dla ustrukturyzowanych danych (kolejki fragmentów, stan sesji) i SyncManager do planowania ponowień uruchamianych gdy urządzenie jest online.
Rejestrowanie i wersjonowanie worker
Zarejestruj raz przy ładowaniu aplikacji i obsługuj aktualizacje jawnie:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then((reg) => reg.addEventListener('updatefound', () => {
const sw = reg.installing;
sw.addEventListener('statechange', () => {
if (sw.state === 'installed' && navigator.serviceWorker.controller) {
// nowa wersja gotowa, poproś użytkownika o odświeżenie
}
});
}));
}
Wersjonuj klucze cache (transfer-v7) żeby nowe wdrożenie unieważniało stare zasoby bez pozostawiania nieaktualnego JavaScript. Klasyczny błąd: index.html buforowany na zawsze, użytkownicy nigdy nie otrzymują aktualizacji. Przyp clam cache do hashy build i czyść je w zdarzeniu activate.
Strategie buforowania dla powłoki aplikacji vs. danych użytkownika
Różne zasoby zasługują na różne strategie:
- Powłoka aplikacji (HTML, CSS, JS, ikony): cache-first z fallbackiem sieciowym. Natychmiastowe ładowania, działa offline.
- Metadane API (
/shares/:id): network-first z fallbackiem cache, TTL 60 sekund. Świeże online, użyteczne offline. - Bajty pliku: nigdy nie buforuj. Pliki często mają wiele gigabajtów, a Cache API ma limity origin (zazwyczaj 60% wolnego dysku).
- Czcionki z CDN: stale-while-revalidate. Szybkie i automatycznie odświeżane.
W obsłudze fetch:
self.addEventListener('fetch', (e) => {
const url = new URL(e.request.url);
if (url.pathname.startsWith('/assets/')) {
e.respondWith(cacheFirst(e.request, 'shell-v7'));
} else if (url.pathname.startsWith('/api/shares/')) {
e.respondWith(networkFirst(e.request, 'api-v1', 60));
}
});
Nigdy nie przechwytuj żądań dla binarnych uploadów pliku — omijaj je sprawdzając e.request.method === 'PUT' i wracając wcześniej. Proxy gigabajtowych PUTów przez worker to katastrofa pamięciowa.
Kolejkowanie nieudanych uploadów przez Background Sync
SyncManager to klucz do odpornych uploadów. Gdy PUT fragmentu nie powiedzie się, schowaj go w IndexedDB i zarejestruj sync:
// w kodzie strony
const reg = await navigator.serviceWorker.ready;
await reg.sync.register('flush-uploads');
// w sw.js
self.addEventListener('sync', (event) => {
if (event.tag === 'flush-uploads') {
event.waitUntil(flushPendingUploads());
}
});
Przeglądarka uruchamia zdarzenie sync gdy sieć wróci, z wykładniczym cofaniem do ~24 godzin. Chrome i Edge obsługują to; Safari dostarczył podzbiór w 17.5 pod flagą "Background Fetch". Dla Safari wróć do ponawiania przy następnym widocznym otwarciu strony przez visibilitychange.
Background Fetch API to osobne narzędzie specjalnie dla dużych operacji na plikach — wyświetla trwałe powiadomienie w interfejsie przeglądarki, żeby użytkownicy mogli śledzić postęp nawet po zamknięciu karty. Warte użycia dla uploadów powyżej 500 MB.
Przechowywanie częściowych uploadów w IndexedDB
IndexedDB to twoja trwała przestrzeń robocza. Otwórz małą bazę danych raz, potem przechowuj metadane sesji plus offsety fragmentów:
const db = await openDB('transfers', 1, {
upgrade(db) {
db.createObjectStore('sessions', { keyPath: 'id' });
db.createObjectStore('chunks', { keyPath: ['sessionId', 'index'] });
}
});
await db.put('sessions', {
id, fileName, fileSize, fileFingerprint, createdAt: Date.now(),
completedIndexes: [], partUrls
});
Nie przechowuj surowych bajtów fragmentów — pochodzą z uchwytu File, który IndexedDB może utrwalać jako referencję structured clone pozostającą ważną między przeładowaniami. Przechowywanie uchwytu unika duplikowania 2 GB bajtów w bazie danych.
Storage origin ma limity: około 60% wolnego dysku na desktopowym Chrome, 1 GB na origin na iOS Safari przed uruchomieniem presji eksmisji. Poproś o navigator.storage.persist() żeby dostać "trwały" bucket, który przeglądarki automatycznie unikają eksmitowania.
Obsługa przejść offline i online
Słuchaj zdarzeń online i offline, zarówno na stronie, jak i w service worker:
// strona
window.addEventListener('online', () => {
ui.showBanner('Powrót online — wznawianie uploadów');
navigator.serviceWorker.controller?.postMessage({ type: 'resume' });
});
window.addEventListener('offline', () => {
ui.showBanner('Offline — uploady wstrzymane');
});
navigator.onLine jest notorycznie zawodny na firmowych portalach captive — raportuje true gdy urządzenie ma połączenie z siecią lokalną, ale brak internetu. Dla wiarygodnego wykrywania wykonaj małe fetch('/ping', { cache: 'no-store' }) z limitem 3 sekund.
Czynienie z tego właściwego PWA
Dostarcz manifest.json z display: standalone, zestawem ikon i start_url: /. Dodaj linki apple-touch-icon dla iOS. Zadeklaruj obsługę pliku, żeby system operacyjny mógł skojarzyć twoją aplikację z konkretnymi rozszerzeniami:
{
"name": "Hex Transfer",
"file_handlers": [{
"action": "/share-target",
"accept": { "application/*": [".pdf", ".zip", ".docx"] }
}]
}
W połączeniu z Web Share Target pozwala to użytkownikom udostępniać pliki z arkusza udostępniania systemu operacyjnego prosto do twojej aplikacji. Na Chrome Android i desktopowym Chromium PWA może zarejestrować się jako domyślny handler dla zadeklarowanych typów pliku. To zamienia stronę przeglądarki w aplikację zachowującą się jak natywne narzędzie uploadu.
Testowanie historii offline
Trzy scenariusze do ręcznego przetestowania, bo automatyczne testy offline są zawodne:
- Zacznij upload 500 MB na szybkim Wi-Fi, przełącz na tryb samolotowy przy 30%, poczekaj 30 sekund, włącz Wi-Fi z powrotem. Upload powinien wznawiać się od miejsca zatrzymania bez działania użytkownika.
- Zacznij upload, zamknij kartę przy 60%, poczekaj 2 minuty, otwórz ponownie. Zaproponuj wznowienie sesji.
- Zacznij upload na mobilnym, zablokuj ekran na 5 minut. Background sync powinien uruchomić się gdy odblokujesz i dokończyć transfer.
Pole wyboru "Offline" w DevTools Chrome i Application > Service Workers > Update on reload są nieocenione. Profile "Throttling" w panelu Network pozwalają symulować Fast 3G i Slow 3G, żeby zobaczyć jak zachowuje się twój interfejs błędów.
HexaTransfer używa Service Worker do buforowania powłoki aplikacji i IndexedDB dla stanu sesji w locie, żeby przeładowania i krótkie okresy offline nie traciły postępu uploadu. Wypróbuj na https://hexatransfer.com — za darmo, bez konta, maks. 10 GB.
Pułapki warte znajomości
Service Workers mają małą stertę pułapek gryzących nowicjuszy: działają tylko przez HTTPS (z wyjątkiem localhost), limity cache różnią się drastycznie między przeglądarkami, iOS Safari nie budzi niezawodnie workerów dla Background Sync, DevTools może agresywnie buforować nieaktualne workery (zawsze klikaj "Bypass for network" podczas tworzenia) i importScripts działa synchronicznie podczas instalacji, więc nigdy nie pobieraj wolnych skryptów zewnętrznych tam. Napisz mały test integracyjny sprawdzający, że worker aktywuje się, przejmuje klientów i serwuje stronę offline — ten jeden test wychwytuje 80% regresji, na które natkniesz się na produkcji.
Wysyłaj duże pliki bezpiecznie z szyfrowaniem end-to-end
Przesyłaj pliki do 10 GB za darmo z szyfrowaniem end-to-end. Bez rejestracji. Twoje pliki są szyfrowane w przeglądarce przed przesłaniem — nikt inny nie może ich odczytać.
Wyślij plik