-
Notifications
You must be signed in to change notification settings - Fork 12
/
downzip-sw.js
139 lines (120 loc) · 4.62 KB
/
downzip-sw.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import '@babel/polyfill'
import WorkerUtils from './WorkerUtils'
import Zip from './zip/Zip'
import ZipUtils from './zip/ZipUtils'
const Utils = new WorkerUtils('DownZipServiceWorker')
// /////////// GLOBAL OBJECTS /////////// //
const zipMap = {}
// ////////// MESSAGE HANDLERS ////////// //
const initialize = (data, ports) => {
Utils.log(`Initialize called: ${JSON.stringify(data)}`)
const {id, files, name} = data
// Decide whether to use zip64
const totalSizeBig = ZipUtils.calculateSize(files)
Utils.log(`Total estimated file size: ${totalSizeBig}`)
const zip64 = (totalSizeBig >= BigInt('0xFFFFFFFF'))
// Start new Zip object and add to the map
zipMap[id] = {
files,
name,
zip: new Zip(zip64),
sizeBig: totalSizeBig
}
// Acknowledge reception
if(ports.length > 0)
ports[0].postMessage({command: 'ACKNOWLEDGE'})
}
// This message is here to keep the service worker from getting killed while downloading.
// TODO: Only send tick while actually downloading
const tick = () => {
Utils.log(`Tock`)
}
// /////////// EVENT HANDLERS /////////// //
self.addEventListener('install', () => {
Utils.log("Installing worker and skip waiting")
skipWaiting()
})
self.addEventListener('activate', () => {
Utils.log("Activating worker and skip waiting")
skipWaiting()
self.clients.claim()
})
self.addEventListener('fetch', async (event) => {
// Get URL and check if it is a download request
const urlParts = event.request.url.split('/')
const lastPart = urlParts[urlParts.length - 1]
if(lastPart.includes('download-')) {
// Get download id
const id = lastPart.replace('download-', '')
Utils.log(`Fetch called for download id: ${id}`)
// Check if initialized
if(!zipMap[id]){
Utils.error(`No zip initialized for id: ${id}`)
return
}
// Respond with the zip outputStream
event.respondWith(new Response(
zipMap[id].zip.outputStream,
{headers: new Headers({
'Content-Type': 'application/octet-stream; charset=utf-8',
'Content-Disposition': `attachment; filename="${zipMap[id].name}.zip"`,
'Content-Length': zipMap[id].sizeBig // This is an approximation, does not take into account the headers
})}
))
// Start feeding zip the downloads
for(let i=0; i<zipMap[id].files.length; i++){
const file = zipMap[id].files[i]
// Start new file in the zip
zipMap[id].zip.startFile(file.name)
// Append all the downloaded data
try {
await new Promise((resolve, reject) => {
fetch(file.downloadUrl).then(response => response.body).then(async (stream) => {
const reader = stream.getReader()
let doneReading = false
while (!doneReading) {
const chunk = await reader.read()
const {done, value} = chunk
if (done) {
// If this stream has finished, resolve and return
resolve()
doneReading = true
} else {
// If not, append data to the zip
zipMap[id].zip.appendData(value)
}
}
}).catch((err) => {
reject(err)
})
})
} catch (e) {
Utils.error(`Error while piping data into zip: ${e.toString()}`)
}
// End file
zipMap[id].zip.endFile()
}
// End zip
zipMap[id].zip.finish()
Utils.log("Done with this zip!")
} else {
Utils.log('Fetch called for a non-download. Doing nothing')
}
})
self.addEventListener('error', (message, url, lineNo) => {
Utils.log(`Error: ${message} at line number: ${lineNo}. Handling URL ${url}`)
return true
})
const messageHandlers = {
'INITIALIZE': initialize,
'TICK': tick
}
self.addEventListener('message', async (event) => {
const {data, ports} = event
const handler = messageHandlers[data.command]
if(handler){
await handler(data.data, ports)
} else {
Utils.error(`Handler for command does not exist: ${data.command}`)
}
})