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) {
|
||||
const chunks = [];
|
||||
let size = 0;
|
||||
const maxPayloadSizeBytes = 8 * 1024 * 1024;
|
||||
|
||||
for await (const chunk of request) {
|
||||
size += chunk.length;
|
||||
if (size > 1024 * 1024) {
|
||||
throw new Error("Payload too large");
|
||||
if (size > maxPayloadSizeBytes) {
|
||||
throw new Error("Payload too large (max 8MB)");
|
||||
}
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
50
app.js
50
app.js
@@ -94,11 +94,11 @@ coverFileInput.addEventListener("change", async (event) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const dataUrl = await fileToDataUrl(file);
|
||||
const dataUrl = await imageFileToOptimizedDataUrl(file);
|
||||
coverUrlInput.value = dataUrl;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert("Impossible de charger cette image.");
|
||||
alert("Impossible de charger/comprimer cette image.");
|
||||
} finally {
|
||||
input.value = "";
|
||||
}
|
||||
@@ -299,7 +299,7 @@ gameForm.addEventListener("submit", async (event) => {
|
||||
return;
|
||||
} catch (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) {
|
||||
if (scannerStatus) {
|
||||
scannerStatus.textContent = message;
|
||||
|
||||
Reference in New Issue
Block a user