try fflate

This commit is contained in:
Gaspard Jankowiak 2026-06-06 05:38:57 +02:00 committed by gapato
commit 0f450e4189
5 changed files with 134 additions and 69 deletions

View file

@ -267,21 +267,66 @@ async function getArchiveViewerHtmlSource() {
return response.text()
}
// Bundle the viewer's JSZip runtime so the exported archive can be browsed offline.
async function getArchiveViewerJsZipSource() {
const candidates = [
"lib/jszip.min.js"
]
// Bundle the viewer's fflate runtime so the exported archive can be browsed offline.
async function getArchiveViewerFflateSource() {
const response = await fetch(chrome.runtime.getURL("lib/fflate.min.js"))
for (const candidate of candidates) {
const response = await fetch(chrome.runtime.getURL(candidate))
if (response.ok) {
return response.text()
}
if (!response.ok) {
throw new Error("Failed to load archive viewer dependency")
}
throw new Error("Failed to load archive viewer dependency")
return response.text()
}
function getFflate() {
if (typeof fflate === "undefined") {
throw new Error("fflate is not available")
}
return fflate
}
async function createZipBlob(entries, onProgress = () => { }) {
const { strToU8, zip } = getFflate()
const archiveEntries = {}
let processedEntries = 0
for (const entry of entries) {
if (entry.data instanceof Blob) {
archiveEntries[entry.path] = new Uint8Array(await entry.data.arrayBuffer())
} else if (entry.data instanceof Uint8Array) {
archiveEntries[entry.path] = entry.data
} else {
archiveEntries[entry.path] = strToU8(String(entry.data ?? ""))
}
entry.data = null
processedEntries += 1
onProgress({
phase: "preparing",
processedEntries,
totalEntries: entries.length
})
}
onProgress({
phase: "generating",
processedEntries,
totalEntries: entries.length
})
const archiveBytes = await new Promise((resolve, reject) => {
zip(archiveEntries, { level: 0, consume: true }, (error, data) => {
if (error != null) {
reject(error)
return
}
resolve(data)
})
})
return new Blob([archiveBytes], { type: "application/zip" })
}
function downloadBlob(blob, fileName) {
@ -470,7 +515,7 @@ function rip(event) {
console.log("Preparing zip archive...")
progressDialog.setStatus("Preparing zip archive...")
const zip = new JSZip()
const archiveEntries = []
const successfulApplicants = []
applicantDetailsResults.forEach((result) => {
@ -479,30 +524,53 @@ function rip(event) {
}
const applicantDetails = result.value
const dirName = getApplicantDirectoryName(applicantDetails)
const applicantDir = zip.folder(`${APPLICATIONS_DIRECTORY}/${dirName}`)
const archiveFiles = getApplicantArchiveFiles(applicantDetails)
successfulApplicants.push(applicantDetails)
archiveFiles.forEach((archiveFile, index) => {
const afile = applicantDetails.application_files[index]
applicantDir.file(archiveFile.fileName, afile.blob)
archiveEntries.push({
path: archiveFile.relativePath,
data: afile.blob
})
})
})
const [viewerHtmlSource, viewerFflateSource] = await Promise.all([
getArchiveViewerHtmlSource(),
getArchiveViewerFflateSource()
])
console.log("Creating applicants.csv")
zip.file("applicants.csv", createApplicantsCsv(successfulApplicants))
archiveEntries.push({
path: "applicants.csv",
data: createApplicantsCsv(successfulApplicants)
})
console.log("Creating index.html")
zip.file("viewer.html", await getArchiveViewerHtmlSource())
archiveEntries.push({
path: "viewer.html",
data: viewerHtmlSource
})
console.log("Adding viewer Javascript to archive")
zip.file("jszip.min.js", await getArchiveViewerJsZipSource())
archiveEntries.push({
path: "fflate.min.js",
data: viewerFflateSource
})
console.log("Generating zip archive...")
const zipBlob = await zip.generateAsync({ type: "blob" })
const zipBlob = await createZipBlob(archiveEntries, ({ phase, processedEntries, totalEntries }) => {
if (phase === "preparing") {
if (processedEntries === totalEntries || processedEntries === 1 || processedEntries % 10 === 0) {
progressDialog.setStatus(`Preparing zip archive (${processedEntries}/${totalEntries})...`)
}
return
}
progressDialog.setStatus("Generating zip archive in background... Firefox should stay responsive.")
})
console.log("Zip archive is ready.")
progressDialog.setStatus("Download ready.")