Chrome App Persistent Filesystem Storage not reliable - google-chrome-app

I am having issues with my google chrome app and filestorage.
The app is run online and gathers files to store offline so that it should be able to function properly offline later. It does this for the most part but sometimes but rarely after a computer restart or restarting the browser it seems to be missing the files in filesystem...
I guess my question is, how do i ensure that Persistent storage remains persistent?
Edit:
Code
Request filesystem
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(
window.PERSISTENT, 200*1024*1024,
function(filesystem) {
directory.fs = filesystem;
//Start Application
},
filesystemerrorHandler
);
Save a File from remote to local filesystem
var xhr = new XMLHttpRequest();
xhr.open('GET', fileurl, true);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function(e) {
if (this.status == 200) {
var blob = new Blob([this.response], {type: blobtype});
directory.fs.root.getFile(name, {create: true}, function(fileEntry) {
fileEntry.createWriter(function(writer) {
writer.onwrite = function(e) {};
writer.onerror = function(e) { console.log("error"); console.log(e); };
var blob = new Blob([xhr.response], {type: blobtype});
writer.write(blob);
var url = fileEntry.toURL();
if ( typeof(callback) == 'function' ) {
//Save url to indexeddb for recall later
//Returns format of: filesystem:chrome-extension://nlipipdnicabdffnohdhhliiajoonmgm/persistent/xxxxxxxxxxxx.png
callback(url);
}
}, filewriteerrorHandler2);
}, filewriteerrorHandler);
}
else {
if ( typeof(callback) == 'function' ) callback(false);
}
};
Recalling the downloaded file example
<img src="filesystem:chrome-extension://nlipipdnicabdffnohdhhliiajoonmgm/persistent/xxxxxxxxxxxx.png">
Now for the most part this will work. However, sometimes, if the computer has been restarted or the browser restarted. If I use the app again the image will not show, this is giving me the impression that the filesystem has been cleared for this app.
What steps, or what area should I be looking at to prevent this from happening?
Thanks.

Increasing the amount of bytes allocated to the app worked.
I was storing more than i was allocating.
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(
window.PERSISTENT, 200*1024*1024, <====
function(filesystem) {
directory.fs = filesystem;
//Start Application
},
filesystemerrorHandler
);

Related

PWA - Cache won't update for offline use

I have a PWA which works fine both online and offline (but only with the initial files). However, the offline cache (let’s say a javascript file) is not being refreshed so whenever I am offline the old javascript file is used, but when online the new version is used.
On an iPad I can use Safari to go to the website and add the PWA to the home page.
If I then go offline, it works fine – all pages work etc.
But if I make a change to say a javascript file (something like adding an alert) and also change the version in my service worker, when I am online the change is reflected but when offline it remains at the older version
To clarify let’s say from the start, on going into a page it alerts “A1”
I then change the javascript to alert “A2” and change the version in the service worker.
If I run the app when online, sure enough the app says New Update Available and All Good (some alerts from the main.js file)
Then when I go into the actual page o the alert says “A2” – so all good.
Then go offline.
The alert still says “A1”
It seems that when online it uses the server latest files but when it tries to use cache the files are old and at the moment seem to be the original files.
I have read many sites on this with no success – some suggest it will sort itself in 24 hours. Some suggest setting the maxage of the service worker to 0 (but how do you do this?). Some say the files need renaming each time they change which seems very clunky.
The service worker is definitely working
main.js
$(document).ready(function () {
'use strict';
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register("/sw.js")
.then(res => {
console.log("service worker registered");
res.onupdatefound = () => {
const installingWorker = res.installing;
installingWorker.onstatechange = () => {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller){
alert("new update available");
forceReload();
}
else {
alert("all good");
}
break;
}
}
}
})
.catch(err => console.log("service worker not registered", err))
}
});
const forceReload = () =>{
console.log("ForceReload");
navigator.serviceWorker
.getRegistrations()
.then((registrations) =>{
console.log(registrations);
//alert("reg");
Promise.all(registrations.map((r) => r.unregister()))
caches.keys().then(function(names) {
for (let name of names)
caches.delete(name);
});
},
)
.then(() => {setTimeout(() => {
location.reload();
}, 500);
})
}
sw.js
let version =5; // update this to send update.
var cacheName = 'cacheV5'
var filesToCache = [
'/',
'/manifest.json',
'/index.html',
'/sales10.html',
'/getdata.html',
....
....
'/js/siteJS/sales10.js',
'/js/siteJS/getdata.js',
'/js/jquery/3.4.1/jquery.min.js',
'/js/bootstrap/bootstrap.min.js',
'/js/bootstrap/popper.min.js'
];
/* Start the service worker and cache all of the app's content */
self.addEventListener('install', function(e) {
self.skipWaiting();
e.waitUntil(
caches.open(cacheName).then(function(cache) {
return cache.addAll(filesToCache);
})
);
});
/* Serve cached content when offline */
self.addEventListener('fetch', function(e) {
e.respondWith(
caches.match(stripQueryStringAndHashFromPath(e.request.url.replace(/^.*\/\/[^\/]+/, ''))).then(function(response) {
return response || fetch(e.request);
})
);
});
function stripQueryStringAndHashFromPath(url) { //added this so when url paramerters passed grabbing the cashed js works
return url.split("?")[0].split("#")[0];
}
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
return true;
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
});

Upload image to firebase storage from React Dropzone (gives invalid Image)

I am using React Dropzone to upload files from React to firebase as shown below:
const onDrop = useCallback((acceptedFiles, fileRejections) => {
//Check if file type is image
//Check if file size < 5MB
//Upload
if (fileRejections.length > 0) {
setError(true);
} else setError(false);
if (acceptedFiles.length > 0) {
const file = acceptedFiles[0];
console.log(file);
setFile({
...file,
preview: URL.createObjectURL(file),
});
setFileUploaded(true);
}
}, []);
and this is my upload handler:
const handleImageUpload = () => {
//Upload Image to Firebase
//Check if file exists
if (file !== null || file !== undefined) {
const storageRef = ref(
Client.storage,
`/db-dev/user-metadata/portfolio/images/first-image.jpg`
);
console.log('Process begins');
uploadBytes(storageRef, file).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
}
};
these two things do the work but I believe for some reason they're not encoding or decoding the image as in firebase storage folder I see image as invalid image.
Can someone help me to understand where things are going wrong? (To make sure file is loaded properly, I am also viewing the file using: preview: URL.createObjectURL(file), and it loads correctly in browser.
For file upload I am following the latest firebase documentation
It sets file type to octet-stream not sure what that means:
Edit 1: I tried to set metadata to image/jpeg:
uploadBytes(storageRef, file, {
contentType: 'image/jpeg',
}).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
But now it shows:
The problem was in this step:
setFile({
...file,
preview: URL.createObjectURL(file),
});
for some reason it wasn't spreading correctly. I changed it to:
setFile({
file:file,
preview: URL.createObjectURL(file),
});
and the upload function to:
const handleImageUpload = () => {
//Upload Image to Firebase
//Check if file exists
if (file !== null || file !== undefined) {
const storageRef = ref(
Client.storage,
`/db-dev/user-metadata/portfolio/images/first-image.jpg`
);
console.log('Process begins');
uploadBytes(storageRef, file.file, {
contentType: file.file.type,
}).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
}
};
and then it worked fine. Although this was a really silly thing on my part but hope this helps someone in future

Improving PWA Page Load

I have a PWA, which is essentially a book reader. As a result, it needs lots of data (viz. the book text) to operate. When analyzed by Lighthouse, it scores poorly on the Page Load Check.
My question is: What methods could I employ to improve the page load, while still ensuring offline functionality?
I could have a minimal start page (e.g., just display a 'Please wait, downloading text' message) and then dynamically download (via injected script tag or AJAX) the JSON data file. However, I'm not sure how I would subsequently ensure that the data is fetched from the cache.
Just wondering how others have handled this issue...
Since this question has gone tumbleweed, I decided to post the results of my attempts.
Based on Jake's article, I used the following script and Chrome DevTools to study service worker events:
'use strict';
let container = null;
let updateFound = false;
let newInstall = false;
window.onload = () => {
container = document.querySelector('.container');
let loading = document.createElement('div');
loading.classList.add('loading');
loading.innerHTML = 'Downloading application.<br>Please wait...';
container.appendChild(loading);
console.log(`window.onload: ${Date.now()}`);
swEvents();
};
let swEvents = () => {
if (navigator.serviceWorker) {
navigator.serviceWorker.ready.then(() => {
console.log(`sw.ready: ${Date.now()}`);
if (!updateFound) {
loadApp();
return;
}
newInstall = true;
console.log(`new install: ${Date.now()}`);
}).catch((error) => {
console.log(`sw.ready error: ${error.message}`);
});
}
navigator.serviceWorker.register('/sw.js').then((reg) => {
reg.onupdatefound = () => {
updateFound = true;
console.log(`reg.updatefound: ${Date.now()}`);
const newWorker = reg.installing;
newWorker.onstatechange = (event) => {
if (event.target.state === 'activated') {
console.log(`nw.activated: ${Date.now()}`);
if (newInstall) {
loadApp();
return;
}
refresh();
}
};
};
}).catch((error) => {
console.log(`reg.error: ${error.message}`);
});
};
let refresh = () => {
console.log(`refresh(): ${Date.now()}`);
// window.location.reload(true);
};
let loadApp = () => {
console.log(`loadApp(): ${Date.now()}`);
let child;
while (child = container.firstChild) {
container.removeChild(child);
}
let message = document.createComment('p');
message.textContent = 'Application loading';
container.appendChild(message);
let tag = document.createElement('script');
tag.src = './app.js';
document.body.appendChild(tag);
};
Along the way, I learned that once a service worker is registered, it immediately begins downloading all cached resources. I had assumed that resources were cached only after the page loaded them. I also found some definitive event patterns to indicate which lifecycle phase was occurring.
For a new install, the following events are logged in the above script:
window.onload -> reg.updatefound -> sw.ready -> nw.activated
For this case, when sw.ready fires, all resources have been cached. At this point, I can switch the app from the 'please wait' phase and dynamically load the cached resources and start the app.
For a simple page refresh, the following events are logged:
window.onload -> sw.ready
This will be the event sequence if the app has already been downloaded and no updates are available. At this point, I can again switch phase and start the app.
For a page refresh when the service worker script has been updated, the following events are logged:
window.onload -> sw.ready -> reg.updatefound -> nw.activated
In this case, when nw.activated fires, all cached resources have been updated. Another page refresh is required to actually load the changes. At this point, the user could be prompted to update. Or the app would update on its own the next time it was started.
By tracking these event patterns, it is easy to tell which lifecycle phase the service worker is in and take the appropriate action.

JQueryMobile saving a form to a txt file

I'm building a app with jquerymobile and I've a page which is a form where I have to fill some info about the field job I have done so I can save it, instead of arriving to the store and fill the paperwork by guessing the time of arrival and the time of the finish.
So, I want to fill the form and when I tap on submit, it saves a txt or another file type on the android phone.
Thanks
This worked for me...
When user clicks the save button
var form_1;
var jsonString;
function saveFormState() {
form_1 = $("#form").find("select,textarea, input").serializeArray();
jsonString = JSON.stringify(form_1);
console.log(jsonString);
getFSToSaveForm();
}
function getFSToSaveForm(){
window.requestFileSystem(LocalFileSystem.PERSISTENT,0 ,function(fileSystem){
var entry=fileSystem.root;
entry.getDirectory('myForms', {create:true, exclusive:false}, function(dirEntry){
dirEntry.getFile('formToSave.json', { create: true, exclusive: false}, saveToJsonFile, onError);
}, onError);
}, onError);
}
function saveToJsonFile(fileEntry){
fileEntry.createWriter(function(writer){
writer.onwrite = function (evt) {
console.log("Wrote to file: " + jsonString);
};
writer.write(jsonString);
}, onError);
}
If you want to restore:
+Read the file and save the read text on a vaiable
Then use some Jquery.
var jsonString;
function getFSToRead(){} //You can find the code in the cordova API http://cordova.apache.org/docs/en/2.5.0/cordova_file_file.md.html
function restoreFormState() {
var newObjectArray ;
newObjectArray = JSON.parse(jsonString);
console.log(newObjectArray.length);
jQuery.each( newObjectArray, function( i, field ) {
$( '#' + field.name).val(field.value);
});
}
Hope that helps

Using PhoneGap to record audio to documents folder on iOS

As part of an iPhone app I'm creating using PhoneGap, I need to be able to use the microphone to record to a new file which is sorted in the apps document folder on the phone.
I think I have the code sorted to actually capture the recording I'm just having trouble creating a blank .wav in the documents folder to record to. According to the PhoneGap API iOS requires that the src file for the audio already exists.
Can anyone help my with the couple of lines of code needed to create this blank file? My code so far is -
function recordAudio() {
var src = "BLANK WAV IN DOCUMENTS FOLDER";
var mediaRec = new Media(src, onSuccess, onError);
// Record audio
mediaRec.startRecord();
// Stop recording after 10 sec
var recTime = 0;
var recInterval = setInterval(function() {
recTime = recTime + 1;
if (recTime >= 10) {
clearInterval(recInterval);
mediaRec.stopRecord();
}
}, 1000);
}
function onSuccess() {
console.log("recordAudio():Audio Success");
}
// onError Callback
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}
$('#record-button').
bind('tap', function(){
recordAudio();
})
You may need to create the file first using the File API.
document.addEventListener("deviceready", function onDeviceReady() {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, function fail(){});
}, false);
var gotFS = function (fileSystem) {
fileSystem.root.getFile("blank.wav",
{ create: true, exclusive: false }, //create if it does not exist
function success(entry) {
var src = entry.toURI();
console.log(src); //logs blank.wav's path starting with file://
},
function fail() {}
);
};
tried using something like this?
var src = "blank.wav"; instead of "BLANK WAV IN DOCUMENTS FOLDER" ?