zips are getting extracted
This commit is contained in:
@@ -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<Record<string, Uint8Array>>((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<ProcessedZipArchive> => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
+44
-8
@@ -1,11 +1,32 @@
|
||||
<script lang="ts">
|
||||
import Card from "$lib/components/Card.svelte";
|
||||
import ZipDropzone from "$lib/components/ZipDropzone.svelte";
|
||||
import { extractZipArchive, type ProcessedZipArchive } from "$lib/zip-processing";
|
||||
|
||||
let selectedZipFiles: File[] = $state([]);
|
||||
let processedZipFiles: ProcessedZipArchive[] = $state([]);
|
||||
let pendingZipCount = $state(0);
|
||||
|
||||
const handleFilesSelected = (files: File[]) => {
|
||||
selectedZipFiles = [...selectedZipFiles, ...files];
|
||||
|
||||
for (const file of files) {
|
||||
void processZipFile(file);
|
||||
}
|
||||
};
|
||||
|
||||
const processZipFile = async (file: File) => {
|
||||
pendingZipCount += 1;
|
||||
|
||||
try {
|
||||
const processedZipArchive = await extractZipArchive(file);
|
||||
processedZipArchive.xjustizNachrichtXml
|
||||
processedZipFiles = [...processedZipFiles, processedZipArchive];
|
||||
} catch (error) {
|
||||
console.error(`Failed to process ${file.name}`, error);
|
||||
} finally {
|
||||
pendingZipCount -= 1;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -15,16 +36,31 @@
|
||||
{:else}
|
||||
<div class="flex min-h-[calc(-180px+100vh)] w-full grow flex-col justify-center gap-4 px-4 py-6">
|
||||
<div class="flex flex-row items-baseline justify-center gap-2">
|
||||
<h2 class="h1 text-[20px] font-bold text-primary-900">ZIP-Dateien geladen</h2>
|
||||
<h2 class="h1 text-[20px] font-bold text-primary-900">ZIP-Dateien werden verarbeitet</h2>
|
||||
</div>
|
||||
|
||||
<ul class="mx-auto w-full max-w-3xl space-y-2">
|
||||
{#each selectedZipFiles as file}
|
||||
<li class="rounded-container bg-primary-50 px-4 py-3 text-[14px] font-medium text-primary-900">
|
||||
{file.name}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="mx-auto flex w-full max-w-3xl flex-col gap-3">
|
||||
<div class="rounded-container bg-primary-50 px-4 py-3 text-[14px] font-medium text-primary-900">
|
||||
{#if pendingZipCount > 0}
|
||||
Noch {pendingZipCount} ZIP-Datei{pendingZipCount === 1 ? "" : "en"} in Bearbeitung.
|
||||
{:else}
|
||||
Alle ZIP-Dateien wurden verarbeitet.
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if processedZipFiles.length > 0}
|
||||
<ul class="space-y-2">
|
||||
{#each processedZipFiles as file}
|
||||
<li class="rounded-container bg-primary-50 px-4 py-3 text-[14px] font-medium text-primary-900">
|
||||
{file.name} - {file.attachments.length} relevante Datei{file.attachments.length === 1 ? "" : "en"}
|
||||
{#if file.xjustizNachrichtXml}
|
||||
- xjustiz_nachricht.xml vorhanden
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
Reference in New Issue
Block a user