In a canvas export, application code chooses the download filename separately from the encoding call. A file named photo.jpg therefore needs a check before you present it as a JPEG.
Check the callback before downloading
MDN documents two results to handle: toBlob uses PNG when the requested format is unsupported, and its callback can receive null if image creation fails. Inspect the returned Blob before constructing the download.
The function below takes a canvas that already contains the pixels you want to export. Loading and decoding a HEIC file would happen before this step.
function exportJpeg(canvas) {
return new Promise((resolve, reject) => {
try {
canvas.toBlob((blob) => {
if (!blob) {
reject(new Error("Canvas export returned no image"));
return;
}
if (blob.type !== "image/jpeg") {
reject(new Error(`Expected image/jpeg, received ${blob.type}`));
return;
}
resolve(blob);
}, "image/jpeg", 0.9);
} catch (error) {
reject(error);
}
});
}
For formats that support lossy compression, the quality argument requests an encoder setting. Here, 0.9 does not mean that 90 percent of the original information survives.
Keep errors attached to the input
Create the .jpg download after exportJpeg resolves. If it rejects, display the error beside the file being processed. For a batch, record attempted and successful exports separately so users can identify the inputs that need another attempt.
Checking Blob.type here verifies the format reported by the browser's export operation. It does not validate the contents of an arbitrary uploaded file, whose declared type can be incorrect. The receiving server needs its own content checks.
I work on Morphio, which has HEIC conversion tools. When checking a conversion interface, I would open a downloaded file and compare its reported format with its extension. I would also look for failed inputs in the result list. These checks apply to other encoders as well as canvas.
API reference: MDN: HTMLCanvasElement.toBlob().
Top comments (1)
Good catch on the null-callback case — that's the one I've actually seen break in production, since people assume toBlob always hands back an image.
One more worth guarding in this same spot: toBlob('image/jpeg') on a canvas with any transparency silently flattens onto black. So the Blob.type check can pass while the JPEG still looks wrong if your source canvas ever has an alpha channel. Checking for alpha before export (or flattening onto white yourself) avoids the surprise.