Compress MSR analysis images before upload
This commit is contained in:
@@ -57,6 +57,57 @@ function resolveCandidateImageUrls(originNode) {
|
||||
return urls;
|
||||
}
|
||||
|
||||
async function blobToOptimizedDataUrl(blob, maxDim = 1024, quality = 0.82) {
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
try {
|
||||
const image = await new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = () => reject(new Error("Failed to decode image blob"));
|
||||
img.src = objectUrl;
|
||||
});
|
||||
|
||||
const width = image.naturalWidth || image.width || 0;
|
||||
const height = image.naturalHeight || image.height || 0;
|
||||
const scale = width > 0 && height > 0 ? Math.min(1, maxDim / Math.max(width, height)) : 1;
|
||||
const targetWidth = Math.max(1, Math.round(width * scale)) || width || 1;
|
||||
const targetHeight = Math.max(1, Math.round(height * scale)) || height || 1;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = targetWidth;
|
||||
canvas.height = targetHeight;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
throw new Error("Could not acquire canvas context");
|
||||
}
|
||||
ctx.drawImage(image, 0, 0, targetWidth, targetHeight);
|
||||
|
||||
const optimizedBlob = await new Promise((resolve, reject) => {
|
||||
canvas.toBlob((result) => {
|
||||
if (result) resolve(result);
|
||||
else reject(new Error("Canvas toBlob failed"));
|
||||
}, "image/jpeg", quality);
|
||||
});
|
||||
|
||||
const dataUrl = await new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.readAsDataURL(optimizedBlob);
|
||||
});
|
||||
|
||||
return {
|
||||
dataUrl,
|
||||
originalWidth: width,
|
||||
originalHeight: height,
|
||||
outputWidth: targetWidth,
|
||||
outputHeight: targetHeight,
|
||||
outputBytes: optimizedBlob.size || 0,
|
||||
};
|
||||
} finally {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
}
|
||||
|
||||
async function imageInputToDataUrl(node, inputName) {
|
||||
const originNode = getOriginNodeForInput(node, inputName);
|
||||
const candidateUrls = resolveCandidateImageUrls(originNode);
|
||||
@@ -72,6 +123,11 @@ async function imageInputToDataUrl(node, inputName) {
|
||||
mimeType: null,
|
||||
blobBytes: 0,
|
||||
dataUrlLength: 0,
|
||||
originalWidth: 0,
|
||||
originalHeight: 0,
|
||||
outputWidth: 0,
|
||||
outputHeight: 0,
|
||||
outputBytes: 0,
|
||||
error: "No candidate image URLs found",
|
||||
},
|
||||
};
|
||||
@@ -87,11 +143,8 @@ async function imageInputToDataUrl(node, inputName) {
|
||||
if (!blob.type.startsWith("image/")) {
|
||||
throw new Error(`Unexpected blob type: ${blob.type || "unknown"}`);
|
||||
}
|
||||
const dataUrl = await new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
const optimized = await blobToOptimizedDataUrl(blob);
|
||||
const dataUrl = optimized.dataUrl;
|
||||
return {
|
||||
dataUrl,
|
||||
debug: {
|
||||
@@ -103,6 +156,11 @@ async function imageInputToDataUrl(node, inputName) {
|
||||
mimeType: blob.type || null,
|
||||
blobBytes: blob.size || 0,
|
||||
dataUrlLength: typeof dataUrl === "string" ? dataUrl.length : 0,
|
||||
originalWidth: optimized.originalWidth,
|
||||
originalHeight: optimized.originalHeight,
|
||||
outputWidth: optimized.outputWidth,
|
||||
outputHeight: optimized.outputHeight,
|
||||
outputBytes: optimized.outputBytes,
|
||||
error: null,
|
||||
},
|
||||
};
|
||||
@@ -122,6 +180,11 @@ async function imageInputToDataUrl(node, inputName) {
|
||||
mimeType: null,
|
||||
blobBytes: 0,
|
||||
dataUrlLength: 0,
|
||||
originalWidth: 0,
|
||||
originalHeight: 0,
|
||||
outputWidth: 0,
|
||||
outputHeight: 0,
|
||||
outputBytes: 0,
|
||||
error: "All candidate image URLs failed",
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user