From 996b1b53600b2103391c25e41748d18635a60248 Mon Sep 17 00:00:00 2001 From: JP Date: Sat, 8 Aug 2026 20:26:58 +0000 Subject: [PATCH] Serve image bytes via the SDK's documented transform The SDK returns a Node IncomingMessage, not a web ReadableStream. undici accepts it (verified against the live bucket, 21882 bytes), but the cast claimed a type it never had. transformToByteArray is the documented API; buffering is fine with uploads capped at 10MB. Co-Authored-By: Claude Opus 5 (1M context) --- src/app/minio-images/[...key]/route.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/app/minio-images/[...key]/route.ts b/src/app/minio-images/[...key]/route.ts index 3b8d2b7..fe39a50 100644 --- a/src/app/minio-images/[...key]/route.ts +++ b/src/app/minio-images/[...key]/route.ts @@ -37,12 +37,20 @@ export async function GET( const object = await getImage(key) if (!object.Body) return new Response(null, { status: 404 }) - return new Response(object.Body as unknown as ReadableStream, { + // The SDK hands back a Node IncomingMessage, not a web ReadableStream. undici + // happens to accept it, but transformToByteArray is the documented API and keeps + // the types honest. Buffering is fine here - uploads are capped at 10MB. + const bytes = await object.Body.transformToByteArray() + // Copy out to a plain ArrayBuffer: a Uint8Array view is not a valid BodyInit. + const body = bytes.buffer.slice( + bytes.byteOffset, + bytes.byteOffset + bytes.byteLength + ) as ArrayBuffer + + return new Response(body, { headers: { "Content-Type": object.ContentType ?? "application/octet-stream", - ...(object.ContentLength - ? { "Content-Length": String(object.ContentLength) } - : {}), + "Content-Length": String(bytes.byteLength), // Keys embed a random UUID, so an object never changes under a given key. // `private` keeps it out of any shared cache now that it is session-gated. "Cache-Control": "private, max-age=31536000, immutable",