Skip to content

Instantly share code, notes, and snippets.

@jacob-ebey
Last active September 18, 2024 19:12
Show Gist options
  • Save jacob-ebey/3a37a86307de9ef22f47aae2e593b56f to your computer and use it in GitHub Desktop.
Save jacob-ebey/3a37a86307de9ef22f47aae2e593b56f to your computer and use it in GitHub Desktop.

Revisions

  1. jacob-ebey revised this gist Jan 5, 2022. 1 changed file with 7 additions and 3 deletions.
    10 changes: 7 additions & 3 deletions image.ts
    Original file line number Diff line number Diff line change
    @@ -40,10 +40,17 @@ export let loader: LoaderFunction = async ({ request }) => {
    return badImageResponse();
    }

    let width = getIntOrNull(url.searchParams.get("width"));
    let height = getIntOrNull(url.searchParams.get("height"));
    let fit: any = url.searchParams.get("fit") || "cover";

    let hash = createHash("sha256");
    hash.update("v1");
    hash.update(request.method);
    hash.update(request.url);
    hash.update(width?.toString() || "0");
    hash.update(height?.toString() || "0");
    hash.update(fit);
    let key = hash.digest("hex");
    let cachedFile = path.resolve(path.join(".cache/images", key + ".webp"));

    @@ -95,9 +102,6 @@ export let loader: LoaderFunction = async ({ request }) => {
    console.error(error);
    });

    let width = getIntOrNull(url.searchParams.get("width"));
    let height = getIntOrNull(url.searchParams.get("height"));
    let fit: any = url.searchParams.get("fit") || "cover";
    if (width || height) {
    sharpInstance.resize(width, height, { fit });
    }
  2. jacob-ebey revised this gist Jan 4, 2022. 1 changed file with 19 additions and 17 deletions.
    36 changes: 19 additions & 17 deletions image.ts
    Original file line number Diff line number Diff line change
    @@ -17,6 +17,7 @@ function badImageResponse() {
    return new Response(buffer, {
    status: 500,
    headers: {
    "Cache-Control": "max-age=0",
    "Content-Type": "image/gif;base64",
    "Content-Length": buffer.length.toFixed(0),
    },
    @@ -40,6 +41,7 @@ export let loader: LoaderFunction = async ({ request }) => {
    }

    let hash = createHash("sha256");
    hash.update("v1");
    hash.update(request.method);
    hash.update(request.url);
    let key = hash.digest("hex");
    @@ -52,22 +54,17 @@ export let loader: LoaderFunction = async ({ request }) => {
    .catch(() => false);

    if (exists) {
    let responseBody = new PassThrough();

    let fileStream = fs.createReadStream(cachedFile);
    fileStream.pipe(responseBody).on("end", () => {
    fileStream.destroy();
    });

    return new NodeResponse(responseBody, {
    return new NodeResponse(fileStream, {
    status: 200,
    headers: {
    "Content-Type": "image/webp",
    "Cache-Control": "public, max-age=31536000, immutable",
    },
    }) as unknown as Response;
    } else {
    console.log("cache skipped for", src);
    console.info("cache skipped for", src);
    }
    } catch (error) {
    console.error(error);
    @@ -77,16 +74,13 @@ export let loader: LoaderFunction = async ({ request }) => {
    let imageBody: Readable | undefined;
    let status = 200;

    if (src.startsWith("/")) {
    if (src.startsWith("/") && (src.length === 1 || src[1] !== "/")) {
    imageBody = fs.createReadStream(path.resolve("public", src.slice(1)));
    } else {
    let imgRequest = new Request(src.toString()) as unknown as NodeRequest;
    imgRequest.agent = new https.Agent({
    rejectUnauthorized: false,
    });
    if (imgRequest.url.startsWith("http://")) {
    console.log({ "imgRequest.url": imgRequest.url });
    }
    let imageResponse = await fetch(imgRequest as unknown as Request);
    imageBody = imageResponse.body as unknown as PassThrough;
    status = imageResponse.status;
    @@ -107,22 +101,30 @@ export let loader: LoaderFunction = async ({ request }) => {
    if (width || height) {
    sharpInstance.resize(width, height, { fit });
    }
    sharpInstance.webp({ lossless: true });
    sharpInstance.webp({ reductionEffort: 6 });

    let responseBody = new PassThrough();
    let imageManipulationStream = imageBody.pipe(sharpInstance);

    await fsp
    .mkdir(path.dirname(cachedFile), { recursive: true })
    .catch(() => {});
    let cacheFileStream = fs.createWriteStream(cachedFile);

    imageManipulationStream.pipe(cacheFileStream);
    imageManipulationStream.pipe(responseBody).on("end", () => {
    imageBody!.destroy();
    await new Promise<void>((resolve, reject) => {
    imageManipulationStream.pipe(cacheFileStream);
    imageManipulationStream.on("end", () => {
    resolve();
    imageBody!.destroy();
    });
    imageManipulationStream.on("error", async (error) => {
    imageBody!.destroy();
    await fsp.rm(cachedFile).catch(() => {});
    });
    });

    return new NodeResponse(responseBody, {
    let fileStream = fs.createReadStream(cachedFile);

    return new NodeResponse(fileStream, {
    status: status,
    headers: {
    "Content-Type": "image/webp",
  3. jacob-ebey revised this gist Dec 31, 2021. No changes.
  4. jacob-ebey created this gist Dec 31, 2021.
    136 changes: 136 additions & 0 deletions image.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,136 @@
    import { createHash } from "crypto";
    import fs from "fs";
    import fsp from "fs/promises";
    import path from "path";
    import https from "https";
    import { PassThrough } from "stream";
    import type { Readable } from "stream";
    import type { LoaderFunction } from "remix";
    import sharp from "sharp";
    import type { Request as NodeRequest } from "@remix-run/node";
    import { Response as NodeResponse } from "@remix-run/node";

    let badImageBase64 = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";

    function badImageResponse() {
    let buffer = Buffer.from(badImageBase64, "base64");
    return new Response(buffer, {
    status: 500,
    headers: {
    "Content-Type": "image/gif;base64",
    "Content-Length": buffer.length.toFixed(0),
    },
    });
    }

    function getIntOrNull(value: string | null) {
    if (value === null) {
    return null;
    }

    return Number.parseInt(value);
    }

    export let loader: LoaderFunction = async ({ request }) => {
    let url = new URL(request.url);

    let src = url.searchParams.get("src");
    if (!src) {
    return badImageResponse();
    }

    let hash = createHash("sha256");
    hash.update(request.method);
    hash.update(request.url);
    let key = hash.digest("hex");
    let cachedFile = path.resolve(path.join(".cache/images", key + ".webp"));

    try {
    let exists = await fsp
    .stat(cachedFile)
    .then((s) => s.isFile())
    .catch(() => false);

    if (exists) {
    let responseBody = new PassThrough();

    let fileStream = fs.createReadStream(cachedFile);
    fileStream.pipe(responseBody).on("end", () => {
    fileStream.destroy();
    });

    return new NodeResponse(responseBody, {
    status: 200,
    headers: {
    "Content-Type": "image/webp",
    "Cache-Control": "public, max-age=31536000, immutable",
    },
    }) as unknown as Response;
    } else {
    console.log("cache skipped for", src);
    }
    } catch (error) {
    console.error(error);
    }

    try {
    let imageBody: Readable | undefined;
    let status = 200;

    if (src.startsWith("/")) {
    imageBody = fs.createReadStream(path.resolve("public", src.slice(1)));
    } else {
    let imgRequest = new Request(src.toString()) as unknown as NodeRequest;
    imgRequest.agent = new https.Agent({
    rejectUnauthorized: false,
    });
    if (imgRequest.url.startsWith("http://")) {
    console.log({ "imgRequest.url": imgRequest.url });
    }
    let imageResponse = await fetch(imgRequest as unknown as Request);
    imageBody = imageResponse.body as unknown as PassThrough;
    status = imageResponse.status;
    }

    if (!imageBody) {
    return badImageResponse();
    }

    let sharpInstance = sharp();
    sharpInstance.on("error", (error) => {
    console.error(error);
    });

    let width = getIntOrNull(url.searchParams.get("width"));
    let height = getIntOrNull(url.searchParams.get("height"));
    let fit: any = url.searchParams.get("fit") || "cover";
    if (width || height) {
    sharpInstance.resize(width, height, { fit });
    }
    sharpInstance.webp({ lossless: true });

    let responseBody = new PassThrough();
    let imageManipulationStream = imageBody.pipe(sharpInstance);

    await fsp
    .mkdir(path.dirname(cachedFile), { recursive: true })
    .catch(() => {});
    let cacheFileStream = fs.createWriteStream(cachedFile);

    imageManipulationStream.pipe(cacheFileStream);
    imageManipulationStream.pipe(responseBody).on("end", () => {
    imageBody!.destroy();
    });

    return new NodeResponse(responseBody, {
    status: status,
    headers: {
    "Content-Type": "image/webp",
    "Cache-Control": "public, max-age=31536000, immutable",
    },
    }) as unknown as Response;
    } catch (error) {
    console.error(error);
    return badImageResponse();
    }
    };
    56 changes: 56 additions & 0 deletions optimized-image.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    import type { ComponentPropsWithoutRef } from "react";

    export function OptimizedImage({
    optimizerUrl = "/resources/image",
    responsive,
    src,
    ...rest
    }: ComponentPropsWithoutRef<"img"> & {
    optimizerUrl?: string;
    responsive?: {
    maxWidth?: number;
    size: { width: number; height?: number };
    }[];
    }) {
    let url = src ? optimizerUrl + "?src=" + encodeURIComponent(src) : src;

    let props: ComponentPropsWithoutRef<"img"> = {
    src: url + `&width=${rest.width || ""}&height=${rest.height || ""}`,
    };

    let largestImageWidth = 0;
    let largestImageSrc: string | undefined;
    if (responsive && responsive.length) {
    let srcSet = "";
    let sizes = "";
    for (let { maxWidth, size } of responsive) {
    if (srcSet) {
    srcSet += ", ";
    }
    let srcSetUrl =
    url + `&width=${size.width}&height=${size.height || ""} ${size.width}w`;
    srcSet += srcSetUrl;

    if (maxWidth) {
    if (sizes) {
    sizes += ", ";
    }
    sizes += `(max-width: ${maxWidth}px) ${size.width}px`;
    }

    if (size.width > largestImageWidth) {
    largestImageWidth = size.width;
    largestImageSrc = srcSetUrl;
    }
    }
    props.srcSet = srcSet;
    props.sizes = sizes;
    props.src = "";
    }

    if (largestImageSrc && (!rest.width || largestImageWidth > rest.width)) {
    props.src = largestImageSrc;
    }

    return <img {...rest} {...props} />;
    }
    30 changes: 30 additions & 0 deletions usage.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    <OptimizedImage
    loading={index === 0 ? "eager" : "lazy"}
    className="w-full h-full object-contain"
    src={image}
    alt=""
    height={480}
    width={480}
    responsive={[
    {
    size: {
    height: 480,
    width: 480,
    },
    maxWidth: 600,
    },
    {
    size: {
    height: 767,
    width: 767,
    },
    maxWidth: 767,
    },
    {
    size: {
    height: 1024,
    width: 1024,
    },
    },
    ]}
    />