Viewing documents offline with WebViewer

There are two parts to loading WebViewer in an offline scenario: loading the web resources (e.g. JS, HTML, CSS, Web Workers, etc) and loading the actual document in the viewer. Loading the resources can be accomplished using service worker, AppCache or using local resources embedded in a native app. Then using IndexedDB makes it straightforward to save and load the actual document.

In this guide, we will be using the service worker to cache and serve WebViewer files and localforage to simplify storing and retrieving documents in web storage. This guide assumes you have some basic knowledge about service workers, so you can read this guide for an overview.

To see a complete project with the code in this guide, visit this repo.

Register a service worker

There isn't anything special about registering a service worker for WebViewer. You can follow the exact steps to register a service worker.

Cache files

After the service worker has been registered, it's time to cache WebViewer files in the service worker. Depending on the type of the documents you are going to load, not every file needs to be cached. In the root folder of the sample project, You can run the following command to generate the list of files to be cached for WebViewer:

JavaScript

1// in the root folder
2node prepare-serviceworker-list.js

This will generate a list of files in a json file in the src folder, which will be used by the service worker. Optionally, you can modify the list of files in WebViewer to be cached by the service worker according to your need.

1// In your service worker file
2const CACHE_NAME = 'YOUR_CACHE_NAME';
3// This file is cached only because we are using this library in this guide
4const localforage = 'path/to/localforage.js';
5
6var assets = [
7 '/',
8 '/style.css',
9 '/index.js',
10 '/index.html',
11 `/scripts/pwacompat.min.js`,
12 `/manifest.json`,
13 '/images/ic_launcher-48.png',
14 '/images/ic_launcher-72.png',
15 '/images/ic_launcher-96.png',
16 '/images/ic_launcher-144.png',
17 '/images/ic_launcher-192.png',
18 '/images/ic_launcher-512.png',
19 'https://fonts.googleapis.com/css?family=Roboto',
20 'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600',
21 'https://fonts.gstatic.com/s/roboto/v19/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2',
22 '/public/lib/webviewer.min.js',
23];
24
25async function getWorkersList() {
26 try {
27 const response = await fetch('/service-worker-list.json');
28 const jsonData = await response.json();
29
30 return jsonData;
31 } catch(err) {
32 console.error('Error fetching workers list');
33 }
34}
35
36self.addEventListener('install', function(event) {
37 event.waitUntil(
38 caches.open(CACHE_NAME)
39 .then(async function(cache) {
40 const { core, ui } = await getWorkersList();
41 return cache.addAll([localforage].concat(
42 core,
43 ui,
44 assets,
45 ));
46 }),
47 );
48});

Return cached responses

WebViewer will append a query string when requesting the worker files depending on if you are using the full API. In order for the service worker to return the correct cached files we need to set the ignoreSearch.

JavaScript

1self.addEventListener('fetch', event => {
2 event.respondWith(
3 caches.match(event.request, { ignoreSearch: true })
4 .then(response => {
5 if (response) {
6 return response;
7 }
8 return fetch(event.request);
9 })
10 );
11});

Store documents

Fetching the document and storing it as a blob are easy using the fetch API and localforage.

JavaScript

1const store = localforage.createInstance({
2 name: 'store'
3});
4const filePath = 'path/to/your/file';
5const fileName = 'fileName';
6
7fetch(filePath)
8 .then(response => response.blob())
9 .then(blob => {
10 store.setItem(fileName, blob);
11 })
12 .catch(error => {
13 console.log(error);
14 });

Load documents

The loadDocument API supports loading a blob so all we need to do is to get the blob from the store and call the API with it.

JavaScript

1store
2 .getItem(fileName)
3 .then((blob) => {
4 viewerInstance.UI.loadDocument(blob, {
5 filename: fileName
6 });
7 });

To see a complete project with the code in this guide, visit this repo.

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales