Od czasu do czasu zdarza się konieczność/przyjemność napisania aplikacji działającej offline, czyli bez dostępu do Internetu. Jeszcze innym razem nasza aplikacja musi działać zarówno offline, jak i online. Jak sobie poradzić z tego typu problemem?
HTML5 przynosi nam bardzo ciekawe rozwiązanie: Offline Web Applications, o czym przeczytasz w niniejszym wpisie.
Ostatnio w ramach pracy napisałem aplikację, która musiała działać offline – udostępniać pewien quiz, gromadzić dane na urządzeniu, a gdy mamy dostęp do Internetu wysyłać zgromadzone informacje do serwera i zapisywać w bazie.
Całość do przetestowania tutaj, lecz polecam otwierać tylko przez Safari (apka przeznaczona na iPady).
Offline Application Caching API
Główną nowością oferowaną przez HTML5 jest CACHE MANIFEST. Jest to specjalny plik, w którym wskazujemy zasoby mające być przechowywane w pamięci podręcznej klienta (i kilka innych rzeczy, o czym dalej).
CACHE MANIFEST pozwala na trwały zapis wybranych plików na urządzeniu, dzięki czemu przy próbie wejścia na stronę bez dostępu do Internetu użytkownik nadal będzie mógł korzystać ze strony – zawartość zostanie po prostu pobrana z cache przeglądarki.
Czyli obrazując to w możliwie najprostszy sposób – będąc online wchodzisz pod jakiś adres WWW. W tym samym momencie wszystkie pliki wskazane w pliku cache manifest są pobierane do pamięci (przyjmijmy, że wszystkie pliki składające się na stronę, pod którą weszliśmy). Odłączasz kabel od Internetu i znowu wchodzisz pod ten sam adres – naszym oczom ukazuje się ta sama strona internetowa, mimo iż nie mamy dostępu do Internetu – całość jest zaciągana z pamięci podręcznej! Cel osiągnięty :-)
Budowa pliku CACHE MANIFEST
Dość teorii, czas zabrać się za konkrety i stworzyć przykładowy plik cache manifestu.
Zacznijmy od poinformowania przeglądarki, że korzystamy z AppCache. W tym celu musimy do elementu HTML dodać atrybut manifest, w którym wskazujemy nasz plik cache manifest:
1 2 | <!DOCTYPE HTML> <html manifest="stronka.appcache"> |
Oczywiście nazwa pliku jest dowolna – nie musi mieć nawet rozszerzenia .appcache, choć dobrym nawykiem jest takie nazewnictwo pliku, by nietrudno się domyślić co robi dany plik w naszym katalogu z witryną.
Przy definiowaniu pliku cache manifest musimy zadbać szczególnie o dwie rzeczy: typ MIME oraz specjalna konstrukcja pliku.
Typ MIME pliku cache manifest
Aby przeglądarka poprawnie obsłużyła nasz cache manifest koniecznie musimy ustawić odpowiedni typ MIME dla naszego pliku. Najprościej jest to zrobić w konfiguracji serwera, choć możemy także wykorzystać przy tym plik .htaccess – wystarczy dodać regułkę:
1 | AddType text/cache-manifest .appcache |
W ten sposób pliki o rozszerzeniu .appcache będą traktowane przez serwer jako pliku typu text/cache-manifest, czyli interpretowane w specjalny sposób.
Ps. jeśli zdecydowałeś się na inny rodzaj rozszerzenia to w powyższym przykładzie appcache zamień na swoje rozszerzenie.
Budowa pliku cache manifest
W najprostszej wersji tak będzie wyglądać nasz plik cache manifest:
1 2 3 4 | CACHE MANIFEST index.html style.css scripts.js |
W pierwszej linijce dajemy tekst: CACHE MANIFEST, natomiast w każdej kolejnej wskazujemy plik, który ma zostać załadowany do pamięci podręcznej (każdy plik w nowej linii). Prawda, że proste? Taka budowa pliku wystarcza w większości prostych aplikacji, także i w mojej, wskazanej powyżej.
Co jeśli potrzebujemy stworzyć bardziej skomplikowaną aplikację offline? Na szczęście HTML5 Offline daje nam szersze możliwości. Zacznijmy od rozbudowy cache manifest:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | CACHE MANIFEST # 2011-07-29:v1 CACHE: index.html style.css scripts.js NETWORK: login.php api.php FALLBACK: *.php /offline.html images/ images/offline.jpg |
Jak widać, powyższy plik posiada trzy główne sekcje:
- CACHE
Pliki domyślnie pobierane do pamięci podręcznej (możemy pominąć nazwę tej sekcji). - NETWORK
Wskazujemy tutaj zasoby, które zawsze wymagają połączenia z siecią. - FALLBACK
W sekcji tej możemy ustawić „treść zastępczą” dla pewnych zasobów. W powyższym przykładzie, przy próbie wywołania jakiegokolwiek pliku z rozszerzeniem php, wyświetlona zostanie zawartość pliku offline.html.
Kolejność sekcji jest nieistotna – możemy nawet kilkukrotnie tworzyć daną sekcję, dodając nowe zasoby.
Znaki specjalne w CACHE MANIFEST
Jak zapewne zauważyłeś, w powyższym przykładzie użyłem specjalnych znaków przy określaniu zasobów w cache manifest. Nie musimy bowiem wypisywać wszystkich plików w CACHE MANIFEST, a możemy użyć wzorców. Poniżej kilka przykładów:
1 2 | FALLBACK: * /offline.html |
Gwiazdka służy do zastępowania wszystkich innych znaków (podobnie jak w wyrażeniach regularnych). W przypadku działania aplikacji w trybie offline, niezależnie od wybranego adresu (dla danej domeny), zawsze zostanie wyświetlony plik offline.html.
1 2 | FALLBACK: download/ /offline.html |
Zostawienie na końcu slasha informuje, że mamy do czynienia z folderem. W tym przypadku nakazujemy, aby cała zawartość folderu download w trybie offline była niedostępna – zamiast tego wyświetlamy użytkownikowi podstronę offline.html.
1 2 | NETWORK: images/*/large/ images/no-large.jpg |
W folderze images przechowujemy wiele podfolderów ze zdjęciami. Każdy z tych folderów zawiera kolejne: large i small. Zdjęcia z folderu small zapisujemy w pamięci podręcznej, natomiast zdjęcia w dużej rozdzielczości wyświetlamy tylko w trybie online (limit buforu jest ograniczony).
W trybie offline, zamiast zdjęć wyświetlamy informację o konieczności przeniesienia się do trybu online (czy cokolwiek innego).
Application Cache & JavaScript
Ok, zbudowaliśmy już plik cache manifest – wskazaliśmy w nim, które pliki chcemy buforować (CACHE), które będą dostępne tylko w trybie online (NETWORK) oraz pliki zastępcze dla wybranych treści w trybie offline (FALLBACK).
Czas dodać jakąś interakcję. Przede wszystkim przydatne okaże się wykrywanie trybu pracy użytkownika – czy pracuje online czy offline. Jeśli bowiem pracuje offline to efekty pracy, którą wykonuje musimy przechowywać w pamięci przeglądarki (z pomocą przychodzi tutaj cookies czy localStorage), a jeśli online to efekty możemy zapisać w bazie danych.
Sprawdzanie trybu pracy jest bardzo proste:
1 2 3 4 5 | if (navigator.onLine) { // online } else { // offline } |
Można też inaczej:
1 2 3 4 5 6 7 | window.addEventListener("offline", function(e) { // offline }, false); window.addEventListener("online", function(e) { // online }, false); |
To nie wszystko. Interfejs ApplicationCache udostępnia wiele informacji i opcji możliwych do wykorzystania w naszej aplikacji:
1 | var appCache = window.applicationCache; |
Status applicationCache
Możemy sprawdzić aktualny status naszej aplikacji. Robimy to w następujący sposób:
1 | var appStatus = window.applicationCache.status; |
Możemy rozpoznać kilka stanów obiektu applicationCache:
- UNCACHED
Aplikacja nie została jeszcze zbuforowana w pamięci urządzenia. - IDLE
W buforze jest najnowsza wersja plików wskazanych przez plik cache manifest i obecnie nie trwa żaden inny proces. - CHECKING
W chwili obecnej trwa sprawdzanie czy w buforze urządzenia są najnowsze pliki. - DOWNLOADING
Trwa pobieranie i zapisywanie plików na urządzeniu. - UPDATEREADY
Pobrano już pliki do pamięci, ale nie wymieniono ich jeszcze z wcześniejszą wersją buforu (należy aktywować przy pomocy metody swapCache). - OBSOLETE
Pliki w buforze są przestarzałe – w pliku cache manifest znajdują się nowe wersje plików.
W zależności od rozpoznanego statusu możemy wywołać pewne zachowania, np.:
1 2 3 4 5 6 | var appCache = window.applicationCache; appCache.update(); // wykonaj aktualizację pamięci podręcznej if (appCache.status == window.applicationCache.UPDATEREADY) { appCache.swapCache(); // aktywuj nowe pliki w pamięci podręcznej } |
W tym przypadku dynamicznie aktualizujemy pamięć podręczną – nakazujemy przeglądarce pobrać nowe pliki z serwera, a jeśli pobieranie przebiegnie prawidłowo to wymieniamy pliki w buforze na nowo pobrane.
Zdarzenia applicationCache
Jak już wcześniej zauważyłeś, możemy sterować naszą aplikacją offline. Do dyspozycji mamy następujące zdarzenia:
- checking
Zdarzenie to jest wywoływane jako pierwsze, w chwili kiedy klient (przeglądarka) sprawdza dostępność pliku cache manifest i szuka plików do aktualizacji. - noupdate
Pliki w pamięci podręcznej są aktualne – nie wymagają aktualizacji. - downloading
Pliki w pamięci podręcznej były nieaktualne, więc trwa proces pobierania nowych zasobów. - progress
Zdarzenie wywoływane po pobraniu każdego kolejnego zasobu wskazanego w pliku cache manifest. - cached
Zasoby wskazane w cache manifest zostały pobrane i znajdują się już w pamięci podręcznej, gotowe do późniejszego użycia. - updateready
Zdarzenie wywoływane w momencie, kiedy na nowo zaktualizowano pamięć podręczną (przez użycie metody update). - obsolete
Podczas pobierania zasobów cache manifest wystąpił błąd 404 lub 410 – proces aktualizacji pamięci podręcznej zakończony niepowodzeniem. - error
Zdarzenie to jest wywoływane w momencie nie odnalezienia któregoś z zasobów wskazanych w pliku cache manifest lub sam plik cache manifest nie uległ żadnej zmianie od ostatniej aktualizacji.
Zdarzenia wywołujemy na obiekcie window.applicationCache, np.:
1 2 3 4 | var appCache = window.applicationCache; appCache.addEventListener('error', function(e) { alert('Nie można pobrać któregoś z plików wskazanych w pliku manifestu. Proces pobierania został przerwany.'); }, false); |
Kilka ważnych uwag
- Łączny rozmiar plików zapisywanych w pamięci podręcznej nie może przekroczyć 5MB (niektóre przeglądarki mają więcej, jednak najniższy z limitów – dla Chrome – wynosi 5MB, więc najbezpieczniej przyjąć ten rozmiar jako górną granicę pamięci podręcznej).
- Jeśli podczas pobierania zasobów cache manifest pojawi się jakikolwiek błąd (np. 404) to cały proces zostaje przerwany i aplikacja działa w oparciu o poprzednio wykonaną kopię (lub jeśli nie ma wcześniejszej kopii – nie działa wcale w trybie offline).
- Plik, z którego wywołujemy CACHE MANIFEST jest automatycznie dodawany do listy plików zapisywanych w pamięci podręcznej. Nie musimy więc dopisywać go do sekcji CACHE, choć dobrym nawykiem jest wrzucenie i tego pliku na listę.
Krótkie podsumowanie
Możliwość tworzenia aplikacji w trybie offline daje niewielkim nakładem pracy mnóstwo korzyści, zarówno dla webdevelopera, jak i użytkownika końcowego:
- Możesz tworzyć aplikacje offline
W czasach coraz większej mobilności jest to niebywała zaleta. Jedziesz metrem lub lecisz samolotem, gdzie nie masz dostępu do Wi-Fi, otwierasz laptopa i pracujesz ze swoją aplikacją offline. Oczywiście w momencie odnalezienia sieci Wi-Fi wyniki są przesyłane i zapisywane w bazie danych (online). - Szybkość wczytywania aplikacji
Przy pierwszej wizycie na stronie zapisujesz w pamięci podręcznej klienta kilka niezmiennych (lub rzadko aktualizowanych plików): style CSS, skrypty JavaScript, obrazki logo i inne. Przy każdej kolejnej wizycie użytkownik nie pobiera plików z sieci, a prosto ze swojego komputera ;) - Minimalizacja zapytań HTTP
To samo co wyżej – możesz część zasobów przerzucić przy pierwszej wizycie do pamięci podręcznej użytkownika, dzięki czemu przy każdej kolejnej zmniejszy się liczba zapytań HTTP (oszczędzasz transfer serwera/klienta, zmniejszasz czas ładowania strony).
Zalet jest niewątpliwie więcej i pewnie nie raz jeszcze wrócę do tego tematu. Również i wsparcie ze strony przeglądarek jest zadowalające:
- Firefox 3.5+
- Safari 4.0+
- Chrome 5.0+
- Opera 10.6+
- iPhone 2.1+
- Android 2.0+
Oczywiście Internet Explorer nie obsługuje appCache, ale kto by się przejmował? ;) Jeśli mimo wszystko zależy komuś na wsparciu dla starszych przeglądarek, także i tutaj znajdzie się HTML5 Cross Browser Polyfills, a dokładniej biblioteka html5-gears. Stworzona przez Brada Neuberg w oparciu o Google Gears (nie testowałem).
Ps. standardowo i tutaj zachęcam wszystkich zainteresowanych do korzystania z HTML5 Offline – technologii, która ułatwia życie zarówno twórcom stron, jak i ich użytkownikom! :-)
Zastanawia mnie czy można użyć w aplikacji 2 metod do przetrzymywania danych: localStorage oraz bazy WebSQL i dzięki temu zwiększyć limit danych do 10 MB.
Świetny artykuł :). Czytałem już trochę wcześniej na temat offline’owych aplikacji, jedną sam napisałem, a i tak dowiedziałem się teraz kilku nowych rzeczy.
To co mi sprawiło problem, to odświeżanie zcache’owanych plików. Nie wiedziałem o możliwości odświeżenia cache’u z poziomu JavaScriptu i jak raz wczytałem aplikację, to miałem później problem :).
Poza tym, funkcjonalność cache’owania aplikacji daje wielkie możliwości. W szczególności umożliwia w śmiesznie prosty sposób tworzenie prostych aplikacji mobile’owych.
@Cezary Tomczak:
Limit 5MB dotyczy tylko i wyłącznie cache manifest i nie ma wpływu na localStorage, cookies czy Web SQL (przynajmniej nie doczytałem nigdzie by było inaczej).
Inna sprawa, że z HTML5 Manifest możesz trzymać w pamięci całe pliki – obrazy, css, javascript (wraz z typem MIME, który później przyczynia się do odpowiedniej interpretacji przez przeglądarkę/serwer), natomiast z ww. musisz mieć je w plain text.
@Piotrek Reinmar Koszuliński:
Dzięki za miłe słowa! ;)
Aby zaktualizować pamięć podręczną powinna wystarczyć jakakolwiek zmiana w pliku cache manifest. Czyli możemy zmienić np. nr rewizji w komentarzu i tyle wystarczy – wszystkie pliki zostaną zaktualizowane w pamięci podręcznej przy najbliższej okazji. Stąd wcześniej dałem taki zapis:
2
# 2011-07-29:v1
Hym :) Powinienem był o tym pomyśleć, że jakakolwiek zmiana wystarczy. Ale dzięki za uświadomienie :).
Ja mam pytanie, czy jest możliwość aby plik posiadający atrybut
nie był automatycznie dodawany do listy plików zapisywanych w pamięci podręcznej, ale tylko z niej korzystał?
@Vampir: z tego co wyczytałem to nie ma takiej możliwości. Zapewne przeglądarka potrzebuje tego pliku i działa to na następującej zasadzie – wpisujesz żądany adres, przeglądarka sprawdza czy nie posiada dla witryny pliku manifestu. Jeśli posiada to szuka plików cache i je wczytuje. Dopiero później wczytuje obiekty spoza pliku manifestu. Jakby więc nie patrzeć, ten plik jest potrzebny przeglądarkom :-)
Choć całkiem możliwe, że działa to jakoś inaczej, jednak nic mi na ten temat nie wiadomo.
Świetny artykuł mam pytanie dotyczące tego czy można to wykorzystać w grach www, np obrazki zapisywały by się na komputerze użytkownika. z góry dzięki
@drais: jak najbardziej można implementować, większość przeglądarek już obsługuje :-) sam też używałem Offline API w kilku grach na iPady i działało bez żadnych zarzutów.
Właśnie przeczytałem powyższy artykuł, gdyż nie byłem do końca pewny czy dobrze zrozumiałem to co jest napisane na ten temat w W3C School. Na razie testuję na jednej „kartce” mojej witryny. Artykuł napisany świetnie. Jedyne co mógłbym dodać to to, że w manifest=”stronka.appcache” może się znaleźć bezwzględny adres pliku, tzn. znaczy chyba, że plik nie musi się znajdować w katalogu głównym witryny. Jeśli się mylę, to proszę o wyjaśnienie.
[…] http://blog.kamilbrenk.pl/tworzenie-aplikacji-z-html5-offline/ […]