I have an offline app based on service worker which allows users to download pdf files offline.
There is a text file which has download path of the pdf files and titles of files (along with css, js, and images path) in a specific format, Application read this text file via ajax and draw the list of all available pdf files (by showing title and download link)
Worker read the same file and put all pdf files in the cache along with text file itself during the install event so that these files can be available offline.
The problem occurs when I update the text file with some new pdf files, the worker is able to update the updated text file in cache because the application give an ajax request to the file and worker able to update it in the cache via fetch event, but new added PDF files not added in the Cache as fetch not triggered for these.
// function to register worker add files in the cache
this.addEventListener('install', event => {
const client = clients.get(event.clientId);
fetch('masterdata.txt')
.then(
function(response) {
response.text().then(function(text) {
var documentFileArray = parseString(text);
var cacheDocs = [];
clients.claim();
var count = 0;
caches.open(currentCache.offline).then(function(cache) {
var total_files = documentFileArray.length;
for(var i =0;i<total_files;i++){
cache.add(documentFileArray[i]).then(function(){
count++
var data = {'count':count,'total':total_files}
passMesagetoClient(clients,data)
})
}
})
})
}
)
.catch(function(err) {
//console.log('Fetch Error :-S', err);
clients.claim();
passMesagetoClient(clients,'failed');
});
});
//comman function to let client know service worker done caching files
function passMesagetoClient(client,data){
client.matchAll().then(all => {
all.map(client => {
client.postMessage({"status":"success","data":data})
})
});
}
//this will check and provide the files from cache if network not avalible
this.addEventListener("fetch", function(event) {
//var url = event.request.url;
clients.claim();
passMesagetoClient(clients,'success');
event.respondWith(
fetch(event.request).then(function(response) {
return caches.open(currentCache.offline).then(function(cache) {
return cache.put(event.request, response.clone()).then(function() {
return response
})
})
}).catch(function(e) {
//console.log(e);
return caches.match(event.request)
})
)
})
Is there any way around to update newly added pdf files in the cache if there is any update in the text file ?
Here is a simple solution. In the fetch
event I check if the request
is for the masterdata.txt
and if so before I overwrite the response
in the cache
I get the old masterdata.txt
from the cache
and if it exists I compare both to check if they are different which means there are new files added then I fetch
those files and add them to the cache
.
// add the files in the array to the cache
async function cacheFiles(filesArray) {
const count = 0;
const cache = await caches.open(currentCache.offline);
const totalFiles = filesArray.length;
for (let i = 0; i < totalFiles; i++) {
await cache.add(filesArray[i]);
count++;
const data = { count: count, total: totalFiles };
passMesagetoClient(clients, data);
}
}
// this will check and provide the files from cache if network not avalible
this.addEventListener("fetch", function(event) {
clients.claim();
passMesagetoClient(clients, "success");
event.respondWith(
fetch(event.request)
.then(async function(newResponse) {
// If this is the text file
if (event.request.url.includes("masterdata.txt")) {
// try to get the text file from the cache
const cachedResponse = await caches.match(event.request);
// If it exists
if (cachedResponse) {
// convert both responses to text
const cachedText = await cachedResponse.text();
const text = await newResponse.text();
// if they are different
if (cachedText !== text) {
// If you can compare the old and new text to get the new urls
// If you can't just compare the two arrays as following
const oldFileArray = parseString(cachedText);
const fileArray = parseString(text);
// get the new urls
const newFileArray = fileArray.filter(
f => !oldFileArray.includes(f)
);
// fetch and cache them like you did in the install event
await cacheFiles(newFileArray);
}
}
}
// add the new response to the cache
const cache = await caches.open(currentCache.offline);
cache.put(event.request, newResponse.clone());
return newResponse;
})
.catch(function(e) {
// console.log(e);
return caches.match(event.request);
})
);
});
I have another solution in mind which depends on how you update the masterdata.txt
. If you do add the new files URLs to masterdata.txt
from the client-side you can simply fetch and cache at the same time.