I'm currently handling the fetch
events in my ServiceWorker as follows:
self.addEventListener('fetch', event => {
// For 'POST' method
if ( event.request.method !== 'GET' && event.request.method !== 'HEAD' && event.request.method === 'POST' ) {
// ...
}
// For 'GET' method
else if ( event.request.method === 'GET' ) {
// ...
}
// For non allowed method
else {
event.respondWith(
new Response('<h1>Method Not Allowed</h1>', {
status: 405,
statusText: 'Method Not Allowed',
headers: new Headers({
'Content-Type': 'text/html'
})
})
)
}
})
This in short reflects what I use on the server side ... only the GET
and POST
methods, refuting other methods.
Going a little further I was able to filter the requests of the POST
method by looking for the appropriate information in event.request.headers
:
// POST JSON
if ( event.request.headers.get('content-type').indexOf('application/json') !== -1 ) {
event.respondWith(
event.request.clone().json().then(json => {
//
})
)
}
// POST FORM URLENCODED
else if ( event.request.headers.get('content-type').indexOf('application/x-www-form-urlencoded') !== -1 ) {
event.respondWith(
event.request.clone().text().then(body => {
let json = JSON.parse('{"' + decodeURIComponent(body).replace(/"/g, '\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}')
})
)
}
// POST FILE
else if ( event.request.headers.get('content-type').indexOf('multipart/form-data') !== -1 ) {
//
}
// OTHERWISE
else {
event.respondWith(
new Response('<h1>Not Acceptable</h1>', {
status: 406,
statusText: 'Not Acceptable',
headers: new Headers({
'Content-Type': 'text/html'
})
})
)
}
As my focus here is to handle uploading file (s), I am currently using .text()
and traversing its lines to know how many files are and extracting information such as: the name attribute, of the file ( filename ) and mimetype (via "Content-Type:"). After this I am using .formData()
to "catch" the contents of the file in order to encode it in base64
and finally save it in IndexedDB
.
The following is what I've been following so far:
event.request.clone().text().then(text => {
let files = [], name, filename
// walk into text lines (FormBoundary)
text.toString().split('\n').forEach((line, i, array) => {
if ( /Content-Disposition:/.test(line) ) {
// get input name and file name
let parts = line.replace(/;/g, '').split(' ')
name = parts[2].replace('name="', '').replace('"', ''),
filename = parts[3].replace('filename="', '').replace('"', '')
}
if ( /Content-Type:/.test(line) ) {
files.push({
name: name,
filename: filename,
mime: line.split(' ')[1]
})
}
// end
if ( i === array.length -1 ) {
const saveFileAsBase64 = function(file) {
event.request.clone().formData().then(formData => {
let read = new FileReader()
read.readAsBinaryString(formData.get(file.name))
read.onloadend = function() {
// save to 'IndexedDB'
let filedata = 'data:${file.mime};base64,${self.btoa(read.result)}'
}
})
}
// one file
if ( files.length === 1 ) {
saveFileAsBase64(files[0])
} // multiple
else {
files.forEach(item => {
saveFileAsBase64(item)
})
}
}
})
})
So if my form sends only files (one or multiple) until "I get turned over", if my form sends more fields like hidden
(a token) or others ( text
, etc ...) then it would have to increase substantially this "gambiarra".
The heart of the matter
Would there be any way to save the form in IndexedDB
?
I have done a lot of searches if I could save as a blob ( .blob()
) or a buffer ( .arrayBuffer()
) and subsequently restore it to the main stream but without success .
I should point out that I'm not looking to use Sync API
, I'm really interested in saving to the indexed database.