Fix: compress cover uploads and raise API payload limit
This commit is contained in:
@@ -27,11 +27,12 @@ function sendJson(response, statusCode, payload) {
|
|||||||
async function readJsonBody(request) {
|
async function readJsonBody(request) {
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
let size = 0;
|
let size = 0;
|
||||||
|
const maxPayloadSizeBytes = 8 * 1024 * 1024;
|
||||||
|
|
||||||
for await (const chunk of request) {
|
for await (const chunk of request) {
|
||||||
size += chunk.length;
|
size += chunk.length;
|
||||||
if (size > 1024 * 1024) {
|
if (size > maxPayloadSizeBytes) {
|
||||||
throw new Error("Payload too large");
|
throw new Error("Payload too large (max 8MB)");
|
||||||
}
|
}
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
}
|
}
|
||||||
|
|||||||
50
app.js
50
app.js
@@ -94,11 +94,11 @@ coverFileInput.addEventListener("change", async (event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dataUrl = await fileToDataUrl(file);
|
const dataUrl = await imageFileToOptimizedDataUrl(file);
|
||||||
coverUrlInput.value = dataUrl;
|
coverUrlInput.value = dataUrl;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
alert("Impossible de charger cette image.");
|
alert("Impossible de charger/comprimer cette image.");
|
||||||
} finally {
|
} finally {
|
||||||
input.value = "";
|
input.value = "";
|
||||||
}
|
}
|
||||||
@@ -299,7 +299,7 @@ gameForm.addEventListener("submit", async (event) => {
|
|||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
alert("Impossible d'enregistrer ce jeu via l'API.");
|
alert(`Impossible d'enregistrer ce jeu via l'API: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -977,6 +977,50 @@ function fileToDataUrl(file) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function imageFileToOptimizedDataUrl(file) {
|
||||||
|
const originalDataUrl = await fileToDataUrl(file);
|
||||||
|
const image = await loadImageFromDataUrl(originalDataUrl);
|
||||||
|
|
||||||
|
const maxWidth = 240;
|
||||||
|
const maxHeight = 320;
|
||||||
|
const scale = Math.min(1, maxWidth / image.width, maxHeight / image.height);
|
||||||
|
const targetWidth = Math.max(1, Math.round(image.width * scale));
|
||||||
|
const targetHeight = Math.max(1, Math.round(image.height * scale));
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = targetWidth;
|
||||||
|
canvas.height = targetHeight;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) {
|
||||||
|
throw new Error("canvas unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
|
||||||
|
let quality = 0.82;
|
||||||
|
let optimized = canvas.toDataURL("image/jpeg", quality);
|
||||||
|
|
||||||
|
// Stay comfortably below API payload thresholds.
|
||||||
|
while (optimized.length > 380_000 && quality > 0.45) {
|
||||||
|
quality -= 0.08;
|
||||||
|
optimized = canvas.toDataURL("image/jpeg", quality);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optimized.length > 520_000) {
|
||||||
|
throw new Error("image too large after compression");
|
||||||
|
}
|
||||||
|
|
||||||
|
return optimized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadImageFromDataUrl(dataUrl) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const image = new Image();
|
||||||
|
image.onload = () => resolve(image);
|
||||||
|
image.onerror = () => reject(new Error("image decode failed"));
|
||||||
|
image.src = dataUrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateScannerStatus(message) {
|
function updateScannerStatus(message) {
|
||||||
if (scannerStatus) {
|
if (scannerStatus) {
|
||||||
scannerStatus.textContent = message;
|
scannerStatus.textContent = message;
|
||||||
|
|||||||
Reference in New Issue
Block a user