perf(read-image-resizer): decode only first 32KB of base64 for dimension parsing

Previously decoded entire image buffer to read headers. Now slices base64
to 32KB prefix before decoding — sufficient for PNG/GIF/WebP/JPEG headers.
Dramatically reduces memory allocation for large images.
This commit is contained in:
YeonGyu-Kim
2026-03-02 14:48:43 +09:00
parent 1a9e7eb305
commit 0dd9ac43ea
2 changed files with 23 additions and 1 deletions

View File

@@ -28,6 +28,13 @@ function createGifDataUrl(width: number, height: number): string {
return `data:image/gif;base64,${buf.toString("base64")}`
}
function createLargePngDataUrl(width: number, height: number, extraBase64Chars: number): string {
const baseDataUrl = createPngDataUrl(width, height)
const base64Data = baseDataUrl.slice(baseDataUrl.indexOf(",") + 1)
const paddedBase64 = `${base64Data}${"A".repeat(extraBase64Chars)}`
return `data:image/png;base64,${paddedBase64}`
}
describe("parseImageDimensions", () => {
it("parses PNG 1x1 dimensions", () => {
//#given
@@ -51,6 +58,17 @@ describe("parseImageDimensions", () => {
expect(result).toEqual({ width: 3000, height: 2000 })
})
it("parses PNG dimensions from a very large base64 payload", () => {
//#given
const dataUrl = createLargePngDataUrl(4096, 2160, 10 * 1024 * 1024)
//#when
const result = parseImageDimensions(dataUrl, "image/png")
//#then
expect(result).toEqual({ width: 4096, height: 2160 })
})
it("parses GIF 1x1 dimensions", () => {
//#given
const dataUrl = GIF_1X1_DATA_URL

View File

@@ -2,6 +2,9 @@ import type { ImageDimensions } from "./types"
import { extractBase64Data } from "../../tools/look-at/mime-type-inference"
const HEADER_BYTES = 32_768
const HEADER_BASE64_CHARS = Math.ceil(HEADER_BYTES / 3) * 4
function toImageDimensions(width: number, height: number): ImageDimensions | null {
if (!Number.isFinite(width) || !Number.isFinite(height)) {
return null
@@ -157,7 +160,8 @@ export function parseImageDimensions(base64DataUrl: string, mimeType: string): I
return null
}
const buffer = Buffer.from(rawBase64, "base64")
const headerBase64 = rawBase64.length > HEADER_BASE64_CHARS ? rawBase64.slice(0, HEADER_BASE64_CHARS) : rawBase64
const buffer = Buffer.from(headerBase64, "base64")
if (buffer.length === 0) {
return null
}