/examples/medialouise-toolkit/media test mode · no data leaves your browser

Upload an image, verified by its bytes

louise-toolkit/media never trusts a file's claimed MIME type — it reads the real format from the leading magic bytes, pulls intrinsic dimensions out of the header without decoding pixels, and serves resized derivatives from the edge. Drop a file on the left and watch the actual toolkit functions run.

live · running now
import { cfImage, mediaUrl, putMedia } from "louise-toolkit/media";

interface MediaEnv {
  MEDIA: R2Bucket; // the bucket uploads land in
  MEDIA_URL: string; // public base URL the bucket is served from
  IMAGES?: ImagesBinding; // optional — sizes AVIF/TIFF the header parser can't
}

// POST multipart/form-data with a `file` field — verify, store, return the URLs.
export async function handleUpload(request: Request, env: MediaEnv): Promise<Response> {
  const form = await request.formData();
  const file = form.get("file");
  if (!(file instanceof File)) return Response.json({ error: "No file" }, { status: 400 });

  // The whole verification: bytes are sniffed for a real image signature, so a
  // file claiming `image/png` in its MIME but carrying JPEG bytes is stored as
  // what it actually is — or rejected outright. `putMedia` writes nothing when it
  // rejects: oversize is 413, non-image is 415.
  const result = await putMedia(env.MEDIA, file, { scope: "web", images: env.IMAGES });
  if (!result.ok) return Response.json({ error: result.error }, { status: result.status });

  // The stored original, plus an on-the-fly resized derivative. `cfImage` only
  // rewrites the path to `/cdn-cgi/image/...` — nothing is re-encoded or stored
  // server-side, so a thumbnail costs no extra bytes in the bucket.
  const url = mediaUrl(env.MEDIA_URL, result.key);
  return Response.json({
    url,
    thumb: cfImage(url, { width: 480, fit: "cover", gravity: "auto", format: "auto" }),
    contentType: result.contentType, // the VERIFIED type, not the client's claim
    width: result.width,
    height: result.height,
  });
}
sliced from real source — never drifts
Try itRename a JPEG to .png and drop it — the sniffer reads the bytes and catches the lie, exactly as the upload route would. Deep-dive doc View source

This page runs the real sniffImageType, imageDimensions andcfImage — they take no bindings, so they behave the same in your browser as on a Worker. The one thing it doesn't do is write to R2. For an upload that actually stores a row and appears in the media library, use the live sandbox.