diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 04658f8..91597f3 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,7 +2,8 @@ - + + @@ -70,7 +79,8 @@ diff --git a/package.json b/package.json index ed546a6..57060a1 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@skeletonlabs/skeleton": "^4.15.2", - "@skeletonlabs/skeleton-svelte": "^4.15.2" + "@skeletonlabs/skeleton-svelte": "^4.15.2", + "fflate": "^0.8.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32f3ff5..7eeef8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@skeletonlabs/skeleton-svelte': specifier: ^4.15.2 version: 4.15.2(svelte@5.56.3) + fflate: + specifier: ^0.8.3 + version: 0.8.3 devDependencies: '@sveltejs/adapter-auto': specifier: ^7.0.1 @@ -594,6 +597,9 @@ packages: picomatch: optional: true + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1570,6 +1576,8 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fflate@0.8.3: {} + fsevents@2.3.3: optional: true diff --git a/src/lib/zip-processing.ts b/src/lib/zip-processing.ts new file mode 100644 index 0000000..fadcdff --- /dev/null +++ b/src/lib/zip-processing.ts @@ -0,0 +1,99 @@ +import { unzip } from "fflate"; + +export type ZipAttachmentKind = "pdf" | "image"; + +export type ZipAttachment = { + name: string; + path: string; + kind: ZipAttachmentKind; + data: Uint8Array; +}; + +export type ProcessedZipArchive = { + name: string; + attachments: ZipAttachment[]; + xjustizNachrichtXml: Uint8Array | null; +}; + +const IMAGE_EXTENSIONS = new Set([ + ".avif", + ".bmp", + ".gif", + ".heic", + ".jpeg", + ".jpg", + ".jp2", + ".png", + ".tif", + ".tiff", + ".webp", +]); + +const ZIP_META_FILE_NAME = "xjustiz_nachricht.xml"; + +const unzipArchive = (data: Uint8Array) => + new Promise>((resolve, reject) => { + unzip(data, (error, files) => { + if (error) { + reject(error); + return; + } + + resolve(files); + }); + }); + +const getBaseName = (path: string) => path.split("/").pop() ?? path; + +const getExtension = (path: string) => { + const baseName = getBaseName(path).toLowerCase(); + const lastDotIndex = baseName.lastIndexOf("."); + + return lastDotIndex === -1 ? "" : baseName.slice(lastDotIndex); +}; + +const isPdf = (path: string) => getExtension(path) === ".pdf"; + +const isImage = (path: string) => IMAGE_EXTENSIONS.has(getExtension(path)); + +export const extractZipArchive = async (file: File): Promise => { + const archiveBytes = new Uint8Array(await file.arrayBuffer()); + const files = await unzipArchive(archiveBytes); + + const attachments: ZipAttachment[] = []; + let xjustizNachrichtXml: Uint8Array | null = null; + + for (const [path, data] of Object.entries(files)) { + const baseName = getBaseName(path); + + if (baseName.toLowerCase() === ZIP_META_FILE_NAME) { + xjustizNachrichtXml = data; + continue; + } + + if (isPdf(path)) { + attachments.push({ + name: baseName, + path, + kind: "pdf", + data, + }); + continue; + } + + if (isImage(path)) { + attachments.push({ + name: baseName, + path, + kind: "image", + data, + }); + } + } + + return { + name: file.name, + attachments, + xjustizNachrichtXml, + }; +}; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 8db6d11..4a7adbe 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,11 +1,32 @@ @@ -15,16 +36,31 @@ {:else}
-

ZIP-Dateien geladen

+

ZIP-Dateien werden verarbeitet

-
    - {#each selectedZipFiles as file} -
  • - {file.name} -
  • - {/each} -
+
+
+ {#if pendingZipCount > 0} + Noch {pendingZipCount} ZIP-Datei{pendingZipCount === 1 ? "" : "en"} in Bearbeitung. + {:else} + Alle ZIP-Dateien wurden verarbeitet. + {/if} +
+ + {#if processedZipFiles.length > 0} +
    + {#each processedZipFiles as file} +
  • + {file.name} - {file.attachments.length} relevante Datei{file.attachments.length === 1 ? "" : "en"} + {#if file.xjustizNachrichtXml} + - xjustiz_nachricht.xml vorhanden + {/if} +
  • + {/each} +
+ {/if} +
{/if} {/snippet}