Der Performance-Check (pruef-tempo.mjs, neu) fand als größte Einzel- ressource ein 1,1-MB-Profilbild, ausgeliefert an jeden Besucher der Startseite und von stimmen.html -- angezeigt wird es handgroß. Ursache: Das öffentliche Stimmen-Formular (stimmen.js) lud Profilbilder ROH hoch. Die Bildaufbereitung vom 27.08. bekam nur die Verwaltung, nicht das öffentliche Formular. So landete das Kamera-Foto in voller Auflösung auf dem Server. - bild-vorbereiten.js (neu): die Verkleinerungs-Funktion, jetzt einmal und parametrisierbar (maxBreite). Verwaltung nutzt weiter 1600px, Avatare 512px. Test pruef-bild-vorbereiten.mjs: 8/8, u.a. 512er-Avatar ~40 KB statt >1 MB. - stimmen.js: verkleinert vor dem Upload (window.bildVorbereiten mit maxBreite 512); fällt die Funktion aus, wird das Original genommen -- kein Upload darf daran scheitern. - stimmen.html: lädt bild-vorbereiten.js; veralteten Kommentar richtiggestellt (behauptete "kein Upload", obwohl es seit 21.08. einen gibt -- mit Bremse, Typ-/Größenlimit, Freigabe-Pflicht). Zwei neue Checks als bleibende Absicherung mit committet: pruef-links.mjs (41 interne Ziele, 0 kaputt) und pruef-assets.mjs (67 Ressourcen, 0 fehlen). NOCH OFFEN: verwaltung.html hat noch eine eigene, identische Inline-Kopie der Funktion -- die Zusammenführung ist ein eigener, testbarer Schritt (das Inline-Skript dort ist groß und die Verwaltung hinter dem Gate schwer live zu testen). Das bestehende 1,1-MB-Bild auf dem Server bleibt, bis es neu hochgeladen/ersetzt wird. Co-Authored-By: Claude Opus 5 <[email protected]>
92 lines
4.0 KiB
JavaScript
92 lines
4.0 KiB
JavaScript
/* =====================================================================
|
|
Verkleinert bild-vorbereiten.js richtig?
|
|
|
|
Läuft in einem echten Browser (Canvas/Image gibt es nur dort). Erzeugt
|
|
Testbilder verschiedener Größe/Format und prüft das Verhalten, auf das
|
|
es ankommt: große Bilder werden auf die vorgegebene Breite gebracht,
|
|
kleine bleiben unangetastet, große PNGs werden zu JPEG, und die
|
|
Avatar-Grenze (512) macht ein Foto deutlich kleiner als die
|
|
Verwaltungs-Grenze (1600).
|
|
===================================================================== */
|
|
import { chromium } from "playwright";
|
|
import { readFileSync } from "node:fs";
|
|
|
|
const quelle = readFileSync("assets/js/bild-vorbereiten.js", "utf8");
|
|
const browser = await chromium.launch();
|
|
const seite = await browser.newPage();
|
|
await seite.setContent("<!doctype html><html><body></body></html>");
|
|
await seite.addScriptTag({ content: quelle });
|
|
|
|
let ok = 0, fehl = 0;
|
|
const pruefe = (name, gut, info) => {
|
|
if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); }
|
|
else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); }
|
|
};
|
|
|
|
/* Hilfsfunktion im Browser: erzeugt ein Testbild als File, jagt es durch
|
|
bildVorbereiten und meldet Maße/Größe/Typ vorher und nachher. */
|
|
const test = (breite, hoehe, typ, opt) => seite.evaluate(async ([breite, hoehe, typ, opt]) => {
|
|
const c = document.createElement("canvas");
|
|
c.width = breite; c.height = hoehe;
|
|
const ctx = c.getContext("2d");
|
|
/* Echtes Zufallsrauschen, damit die Testbilder realistisch groß werden
|
|
(ein Muster oder eine Fläche würde PNG so stark komprimieren, dass die
|
|
"großes PNG"-Grenze von 400 KB nie erreicht wird -- dann bliebe der
|
|
PNG->JPEG-Pfad ungetestet). */
|
|
const img = ctx.createImageData(breite, hoehe);
|
|
for (let i = 0; i < img.data.length; i += 4) {
|
|
img.data[i] = Math.random() * 256; img.data[i+1] = Math.random() * 256;
|
|
img.data[i+2] = Math.random() * 256; img.data[i+3] = 255;
|
|
}
|
|
ctx.putImageData(img, 0, 0);
|
|
const blob = await new Promise((f) => c.toBlob(f, typ, 0.95));
|
|
const datei = new File([blob], "test." + (typ === "image/png" ? "png" : "jpg"), { type: typ });
|
|
|
|
const raus = await window.bildVorbereiten(datei, opt);
|
|
/* Maße des Ergebnisses auslesen. */
|
|
const masse = await new Promise((f) => {
|
|
const b = new Image();
|
|
b.onload = () => f({ w: b.naturalWidth, h: b.naturalHeight });
|
|
b.src = URL.createObjectURL(raus);
|
|
});
|
|
return { vorherBytes: datei.size, nachherBytes: raus.size, w: masse.w, h: masse.h, typ: raus.type };
|
|
}, [breite, hoehe, typ, opt]);
|
|
|
|
console.log("\n=== Großes Foto, Verwaltungs-Grenze (1600) ===");
|
|
{
|
|
const r = await test(2400, 1350, "image/jpeg", { maxBreite: 1600 });
|
|
pruefe("wird auf 1600 Breite gebracht", r.w === 1600, `${r.w}px breit`);
|
|
pruefe("wird kleiner", r.nachherBytes < r.vorherBytes, `${Math.round(r.vorherBytes/1024)} -> ${Math.round(r.nachherBytes/1024)} KB`);
|
|
}
|
|
|
|
console.log("\n=== Großes Foto, Avatar-Grenze (512) ===");
|
|
{
|
|
const r = await test(2400, 1350, "image/jpeg", { maxBreite: 512 });
|
|
pruefe("wird auf 512 Breite gebracht", r.w === 512, `${r.w}px breit`);
|
|
pruefe("Avatar ist DEUTLICH kleiner als 1600er", r.nachherBytes < 80 * 1024, `${Math.round(r.nachherBytes/1024)} KB`);
|
|
}
|
|
|
|
console.log("\n=== Kleines Bild bleibt unangetastet ===");
|
|
{
|
|
const r = await test(300, 300, "image/jpeg", { maxBreite: 512 });
|
|
pruefe("300px-Bild behält 300px", r.w === 300, `${r.w}px`);
|
|
}
|
|
|
|
console.log("\n=== Großes PNG wird zu JPEG ===");
|
|
{
|
|
const r = await test(2000, 1200, "image/png", { maxBreite: 1600 });
|
|
pruefe("Format wird JPEG", r.typ === "image/jpeg", r.typ);
|
|
pruefe("auf 1600 gebracht", r.w === 1600, `${r.w}px`);
|
|
}
|
|
|
|
console.log("\n=== Seitenverhältnis bleibt erhalten ===");
|
|
{
|
|
const r = await test(1600, 900, "image/jpeg", { maxBreite: 800 });
|
|
const verh = (r.w / r.h).toFixed(2);
|
|
pruefe("16:9 bleibt 16:9", verh === "1.78", `${r.w}x${r.h} (${verh})`);
|
|
}
|
|
|
|
await browser.close();
|
|
console.log(`\n===== ${ok} bestanden, ${fehl} fehlgeschlagen =====`);
|
|
process.exitCode = fehl ? 1 : 0;
|