Skip to content

Commit b385f95

Browse files
committed
Use fflate instead of JSZip.
1 parent a2ab58b commit b385f95

7 files changed

Lines changed: 77 additions & 95 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ Firefox doesn't support PWA installation natively. Use the [PWAs for Firefox](ht
169169
- Caches the application shell for offline use.
170170
- Intercepts requests to `/viewer/*` and serves extracted ZIP content from memory.
171171

172-
2. **ZIP processing** (`js/app.js` + `js/zip.worker.js`): Uses JSZip in a Web Worker to extract content in a background thread without blocking the UI. Files are sent to the Service Worker as ArrayBuffers using Transferable objects (zero-copy transfer).
172+
2. **ZIP processing** (`js/app.js` + `js/zip.worker.js`): Uses fflate in a Web Worker to extract content in a background thread without blocking the UI. Files are sent to the Service Worker as ArrayBuffers using Transferable objects (zero-copy transfer).
173173

174174
3. **Content display**: An iframe loads `/viewer/index.html`. The Service Worker intercepts this request and serves the corresponding file from the extracted content.
175175

@@ -244,7 +244,7 @@ exeviewer/
244244
├── vendor/ # Third-party libraries
245245
│ ├── bootstrap/ # Bootstrap 5.3.2
246246
│ ├── bootstrap-icons/# Bootstrap Icons 1.11.1
247-
│ └── jszip/ # JSZip 3.10.1
247+
│ └── fflate/ # fflate 0.8.2
248248
└── scripts/
249249
└── generate-icons.js # Icon generation script (requires Node.js + sharp)
250250
```
@@ -265,7 +265,7 @@ You should have received a copy of the GNU Affero General Public License along w
265265

266266
- [Bootstrap](https://getbootstrap.com/) v5.3.2 - Copyright 2011-2023 The Bootstrap Authors - MIT License
267267
- [Bootstrap Icons](https://icons.getbootstrap.com/) v1.11.1 - Copyright 2019-2023 The Bootstrap Authors - MIT License
268-
- [JSZip](https://stuk.github.io/jszip/) v3.10.1 - Copyright 2009-2016 Stuart Knightley - MIT License or GPLv3
268+
- [fflate](https://github.com/101arrowz/fflate) v0.8.2 - Copyright 2020 Arjun Barrett - MIT License
269269

270270
### Related projects
271271

README_es.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ Firefox no soporta la instalación de PWA de forma nativa. Usa la extensión [PW
169169
- Almacena en caché la estructura de la aplicación para uso sin conexión.
170170
- Intercepta las peticiones a `/viewer/*` y sirve el contenido ZIP extraído desde memoria.
171171

172-
2. **Procesamiento ZIP** (`js/app.js` + `js/zip.worker.js`): Usa JSZip en un Web Worker para extraer el contenido en un hilo secundario sin bloquear la interfaz. Los ficheros se envían al Service Worker como ArrayBuffers usando objetos Transferable (transferencia sin copia).
172+
2. **Procesamiento ZIP** (`js/app.js` + `js/zip.worker.js`): Usa fflate en un Web Worker para extraer el contenido en un hilo secundario sin bloquear la interfaz. Los ficheros se envían al Service Worker como ArrayBuffers usando objetos Transferable (transferencia sin copia).
173173

174174
3. **Visualización del contenido**: Un iframe carga `/viewer/index.html`. El Service Worker intercepta esta petición y sirve el fichero correspondiente del contenido extraído.
175175

@@ -244,7 +244,7 @@ exeviewer/
244244
├── vendor/ # Bibliotecas de terceros
245245
│ ├── bootstrap/ # Bootstrap 5.3.2
246246
│ ├── bootstrap-icons/# Bootstrap Icons 1.11.1
247-
│ └── jszip/ # JSZip 3.10.1
247+
│ └── fflate/ # fflate 0.8.2
248248
└── scripts/
249249
└── generate-icons.js # Script de generación de iconos (requiere Node.js + sharp)
250250
```
@@ -265,7 +265,7 @@ Deberías haber recibido una copia de la Licencia Pública General Affero de GNU
265265

266266
- [Bootstrap](https://getbootstrap.com/) v5.3.2 - Copyright 2011-2023 The Bootstrap Authors - Licencia MIT
267267
- [Bootstrap Icons](https://icons.getbootstrap.com/) v1.11.1 - Copyright 2019-2023 The Bootstrap Authors - Licencia MIT
268-
- [JSZip](https://stuk.github.io/jszip/) v3.10.1 - Copyright 2009-2016 Stuart Knightley - Licencia MIT o GPLv3
268+
- [fflate](https://github.com/101arrowz/fflate) v0.8.2 - Copyright 2020 Arjun Barrett - Licencia MIT
269269

270270
### Proyectos relacionados
271271

index.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,6 @@ <h5 class="modal-title" id="shareModalLabel" data-i18n="share.modalTitle">Share
231231
</div>
232232
</div>
233233

234-
<!-- JSZip Library -->
235-
<script src="vendor/jszip/jszip.min.js"></script>
236-
237234
<!-- Bootstrap JS -->
238235
<script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
239236

js/zip.worker.js

Lines changed: 68 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
11
/**
22
* eXeViewer - ZIP Extraction Web Worker
3-
* Handles ZIP file extraction in a background thread to avoid blocking the UI
3+
* Handles ZIP file extraction in a background thread using fflate
44
*/
55

6-
// Import JSZip library
7-
importScripts('../vendor/jszip/jszip.min.js');
6+
// Import fflate library
7+
importScripts('../vendor/fflate/fflate.min.js');
8+
9+
/**
10+
* Find common prefix in file paths (for ZIPs with a root folder)
11+
* @param {string[]} fileList - List of file paths
12+
* @returns {string} Common prefix or empty string
13+
*/
14+
function findCommonPrefix(fileList) {
15+
const htmlFiles = fileList.filter(f => f.endsWith('.html') || f.endsWith('.htm'));
16+
if (htmlFiles.length === 0) return '';
17+
18+
const indexFile = htmlFiles.find(f => f === 'index.html' || f.endsWith('/index.html'));
19+
if (indexFile && indexFile.includes('/')) {
20+
const parts = indexFile.split('/');
21+
if (parts.length === 2) {
22+
return parts[0] + '/';
23+
}
24+
}
25+
return '';
26+
}
827

928
/**
1029
* Handle messages from the main thread
@@ -16,87 +35,65 @@ self.onmessage = async function(e) {
1635
// Notify start of reading
1736
self.postMessage({ type: 'progress', message: 'reading' });
1837

19-
const zip = await JSZip.loadAsync(file);
20-
const files = {};
21-
const fileList = Object.keys(zip.files);
38+
// Read file as ArrayBuffer
39+
const arrayBuffer = await file.arrayBuffer();
40+
const uint8Array = new Uint8Array(arrayBuffer);
2241

23-
console.log(`[Worker] ZIP contains ${fileList.length} entries`);
24-
25-
// Find common prefix (if files are in a subdirectory)
26-
let commonPrefix = '';
27-
const htmlFiles = fileList.filter(f => f.endsWith('.html') || f.endsWith('.htm'));
28-
29-
if (htmlFiles.length > 0) {
30-
// Check if there's an index.html at root or in a subfolder
31-
const indexFile = htmlFiles.find(f =>
32-
f === 'index.html' ||
33-
f.endsWith('/index.html')
34-
);
35-
36-
if (indexFile && indexFile.includes('/')) {
37-
// Get the prefix (folder containing index.html)
38-
const parts = indexFile.split('/');
39-
if (parts.length === 2) {
40-
commonPrefix = parts[0] + '/';
41-
}
42+
// Use fflate to unzip
43+
fflate.unzip(uint8Array, (err, unzipped) => {
44+
if (err) {
45+
console.error('[Worker] fflate error:', err);
46+
self.postMessage({ type: 'error', message: err.message });
47+
return;
4248
}
43-
}
4449

45-
console.log(`[Worker] Common prefix: "${commonPrefix}"`);
50+
const fileList = Object.keys(unzipped);
51+
console.log(`[Worker] ZIP contains ${fileList.length} entries`);
4652

47-
let processed = 0;
48-
const total = fileList.length;
49-
const transferables = [];
53+
// Find common prefix (if files are in a subdirectory)
54+
const commonPrefix = findCommonPrefix(fileList);
55+
console.log(`[Worker] Common prefix: "${commonPrefix}"`);
5056

51-
for (const relativePath of fileList) {
52-
const zipEntry = zip.files[relativePath];
57+
const files = {};
58+
const transferables = [];
5359

54-
// Skip directories
55-
if (zipEntry.dir) {
56-
processed++;
57-
continue;
58-
}
60+
for (const path of fileList) {
61+
const content = unzipped[path];
5962

60-
// Get the normalized path (remove common prefix if exists)
61-
let normalizedPath = relativePath;
62-
if (commonPrefix && relativePath.startsWith(commonPrefix)) {
63-
normalizedPath = relativePath.substring(commonPrefix.length);
64-
}
63+
// Skip empty entries (directories in fflate have zero-length arrays)
64+
if (content.length === 0) {
65+
continue;
66+
}
6567

66-
// Skip empty paths
67-
if (!normalizedPath) {
68-
processed++;
69-
continue;
70-
}
68+
// Get the normalized path (remove common prefix if exists)
69+
let normalizedPath = path;
70+
if (commonPrefix && path.startsWith(commonPrefix)) {
71+
normalizedPath = path.substring(commonPrefix.length);
72+
}
7173

72-
// Read file as ArrayBuffer (more efficient than base64)
73-
try {
74-
const content = await zipEntry.async('arraybuffer');
75-
files[normalizedPath] = content;
76-
transferables.push(content);
77-
} catch (error) {
78-
console.warn(`[Worker] Error reading ${relativePath}:`, error);
79-
}
74+
// Skip empty paths
75+
if (!normalizedPath) {
76+
continue;
77+
}
8078

81-
processed++;
82-
if (processed % 50 === 0) {
83-
self.postMessage({
84-
type: 'progress',
85-
message: 'extracting',
86-
processed,
87-
total
88-
});
79+
// Convert Uint8Array to ArrayBuffer for transfer
80+
const buffer = content.buffer.slice(
81+
content.byteOffset,
82+
content.byteOffset + content.byteLength
83+
);
84+
files[normalizedPath] = buffer;
85+
transferables.push(buffer);
8986
}
90-
}
9187

92-
const fileCount = Object.keys(files).length;
93-
console.log(`[Worker] Extracted ${fileCount} files`);
88+
const fileCount = Object.keys(files).length;
89+
console.log(`[Worker] Extracted ${fileCount} files`);
9490

95-
// Transfer ArrayBuffers to main thread (zero-copy transfer)
96-
self.postMessage(
97-
{ type: 'complete', files, fileCount },
98-
transferables
99-
);
91+
// Transfer ArrayBuffers to main thread (zero-copy transfer)
92+
self.postMessage(
93+
{ type: 'complete', files, fileCount },
94+
transferables
95+
);
96+
});
10097

10198
} catch (error) {
10299
console.error('[Worker] Error:', error);

sw.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Handles PWA caching and serves extracted ZIP content from memory/IndexedDB
44
*/
55

6-
const SW_VERSION = '1.6.0';
6+
const SW_VERSION = '1.7.0';
77
const CACHE_NAME = `exeviewer-v${SW_VERSION}`;
88

99
// IndexedDB configuration
@@ -30,7 +30,7 @@ const APP_SHELL_FILES = [
3030
'./vendor/bootstrap-icons/bootstrap-icons.min.css',
3131
'./vendor/bootstrap-icons/fonts/bootstrap-icons.woff2',
3232
'./vendor/bootstrap-icons/fonts/bootstrap-icons.woff',
33-
'./vendor/jszip/jszip.min.js'
33+
'./vendor/fflate/fflate.min.js'
3434
];
3535

3636
// In-memory storage for the extracted ZIP contents

vendor/fflate/fflate.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/jszip/jszip.min.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)