Skip to content

Instantly share code, notes, and snippets.

@lambdaxyzt
Last active September 18, 2024 19:34
Show Gist options
  • Save lambdaxyzt/1ad05e98b73aab71fc7714d150a1480a to your computer and use it in GitHub Desktop.
Save lambdaxyzt/1ad05e98b73aab71fc7714d150a1480a to your computer and use it in GitHub Desktop.
Remix Image Component ( use cache 'cacashe library' , stream base , sharp library )
// resource route component
import React from "react";
import { PassThrough } from "node:stream"
import fs from "node:fs"
import {createReadableStreamFromReadable} from "@remix-run/node"
import { defaultQuality,widths,mainImageReadStream,generatedImageReadstream, isThereImage,BadImageResponse } from "../../util/image.server"
export const loader = async ({ request }) => {
const url = new URL(request.url);
const src = url.searchParams.get("src");
const width = url.searchParams.get("w");
if (!src || !width ) {
return BadImageResponse();
}
try{
if(!await isThereImage(src)) {
for (const width of widths) {
await generatedImageReadstream(await mainImageReadStream(src),src,width,defaultQuality)
}
} else {
const {size,fileStream,hash} = await generatedImageReadstream(await mainImageReadStream(src),src,width,defaultQuality)
const body = new PassThrough()
const stream = fileStream
stream.on('error', err => body.end(err))
stream.on('end', () => body.end())
stream.pipe(body)
return new Response(createReadableStreamFromReadable(body), {
status: 200,
headers: {
'content-type': 'image/webp',
'content-length': String(size),
'content-disposition': `inline; filename="${hash}.webp"`,
'cache-control': 'public, max-age=31536000, immutable',
},
})
}
} catch(e) {
console.log(e)
return BadImageResponse();
}
}
/*
# tnx to
- https://github.com/ccssmnn : https://github.com/remix-run/remix/discussions/2905#discussioncomment-2596810
- https://stackoverflow.com/a/51302466/12814525
- https://github.com/epicweb-dev/web-forms
*/
// library : sharp , cacache
// server side
import sharp from "sharp";
import axios from "axios";
import path from "node:path";
import cacache from "cacache";
import { finished } from "node:stream/promises"
import {widths as widths_,defaultQuality as defaultQuality_} from "./variable"
export const cachePathImage = path.resolve('./.cache/image')
export const cachePathMainImage = path.resolve('./.cache/image/tmp')
export const widths = widths_
export const defaultQuality = defaultQuality_
export const BadImageResponse = () => {
const buffer = Buffer.from(
"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0xNC4yNjM5IDE1LjkzNzVMMTIuNTk1OCAxNC4yODM0QzExLjc5MDkgMTMuNDg1MSAxMS4zODg0IDEzLjA4NiAxMC45MjY2IDEyLjk0MDFDMTAuNTIwNCAxMi44MTE4IDEwLjA4MzggMTIuODE2NSA5LjY4MDQ4IDEyLjk1MzZDOS4yMjE4OCAxMy4xMDk1IDguODI4MTQgMTMuNTE3MiA4LjA0MDY4IDE0LjMzMjZMNC4wNDQwOSAxOC4yODAxTTE0LjI2MzkgMTUuOTM3NUwxNC42MDUzIDE1LjU5OUMxNS40MTEyIDE0Ljc5OTggMTUuODE0MSAxNC40MDAyIDE2LjI3NjUgMTQuMjU0M0MxNi42ODMxIDE0LjEyNiAxNy4xMiAxNC4xMzExIDE3LjUyMzYgMTQuMjY4N0MxNy45ODI0IDE0LjQyNTEgMTguMzc2MSAxNC44MzM5IDE5LjE2MzQgMTUuNjUxNEwyMCAxNi40OTM0TTE0LjI2MzkgMTUuOTM3NUwxOC4yNzUgMTkuOTU2NU0xOC4yNzUgMTkuOTU2NUMxNy45MTc2IDIwIDE3LjQ1NDMgMjAgMTYuOCAyMEg3LjJDNi4wNzk4OSAyMCA1LjUxOTg0IDIwIDUuMDkyMDIgMTkuNzgyQzQuNzE1NjkgMTkuNTkwMyA0LjQwOTczIDE5LjI4NDMgNC4yMTc5OSAxOC45MDhDNC4xMjc5NiAxOC43MzEzIDQuMDc1MTIgMTguNTMyMSA0LjA0NDA5IDE4LjI4MDFNMTguMjc1IDE5Ljk1NjVDMTguNTI5MyAxOS45MjU2IDE4LjczMDEgMTkuODcyNyAxOC45MDggMTkuNzgyQzE5LjI4NDMgMTkuNTkwMyAxOS41OTAzIDE5LjI4NDMgMTkuNzgyIDE4LjkwOEMyMCAxOC40ODAyIDIwIDE3LjkyMDEgMjAgMTYuOFYxNi40OTM0TTQuMDQ0MDkgMTguMjgwMUM0IDE3LjkyMjEgNCAxNy40NTc1IDQgMTYuOFY3LjJDNCA2LjA3OTkgNCA1LjUxOTg0IDQuMjE3OTkgNS4wOTIwMkM0LjQwOTczIDQuNzE1NjkgNC43MTU2OSA0LjQwOTczIDUuMDkyMDIgNC4yMTc5OUM1LjUxOTg0IDQgNi4wNzk4OSA0IDcuMiA0SDE2LjhDMTcuOTIwMSA0IDE4LjQ4MDIgNCAxOC45MDggNC4yMTc5OUMxOS4yODQzIDQuNDA5NzMgMTkuNTkwMyA0LjcxNTY5IDE5Ljc4MiA1LjA5MjAyQzIwIDUuNTE5ODQgMjAgNi4wNzk5IDIwIDcuMlYxNi40OTM0TTE3IDguOTk5ODlDMTcgMTAuMTA0NSAxNi4xMDQ2IDEwLjk5OTkgMTUgMTAuOTk5OUMxMy44OTU0IDEwLjk5OTkgMTMgMTAuMTA0NSAxMyA4Ljk5OTg5QzEzIDcuODk1MzIgMTMuODk1NCA2Ljk5OTg5IDE1IDYuOTk5ODlDMTYuMTA0NiA2Ljk5OTg5IDE3IDcuODk1MzIgMTcgOC45OTk4OVoiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4NCjwvc3ZnPg==",
"base64"
);
return new Response(buffer, {
status: 500,
headers: {
"Cache-Control": "max-age=0",
"Content-Type": "data:image/svg+xml;base64",
"Content-Length": buffer.length.toFixed(0),
},
});
};
export const isThereImage = async (url) => {
const isContentThere = await cacache.get(cachePathMainImage, url).then(
() => {
return true
},
() => {
return false
}
)
return isContentThere
};
export const mainImageReadStream = async (url) => {
const isContentThere = await isThereImage(url)
if (!isContentThere) {
const response = await axios({
method: 'GET',
url: url,
responseType: 'stream'
})
// pipe the result stream into a file on disc
await response.data.pipe(cacache.put.stream(cachePathMainImage, url).on('integrity', d => console.log(`integrity digest is ${d}`)))
}
return cacache.get.stream(
cachePathMainImage, url
)
}
export const generatedImageReadstream = async (mainImageReadstream, url, width, quality) => {
const urlKey = `url:${url},w:${width},q:${quality}`
const isContentThere = await isThereImage(urlKey)
if (!isContentThere) {
const pipeline = sharp();
pipeline
.resize(parseInt(width))
.webp({
quality: parseInt(quality),
})
// pip it to cache by cacache library
.pipe(
cacache.put.stream(
cachePathImage, urlKey
)
);
// transform then cache it accroding to pipline
await finished(mainImageReadstream.pipe(pipeline));
}
const {size,integrity} = await cacache.get.info(cachePathImage, urlKey)
const fileStream = cacache.get.stream(
cachePathImage, urlKey
)
return {
fileStream,
size,
hash:integrity,
}
};
/*
# tnx to
- https://github.com/ccssmnn : https://github.com/remix-run/remix/discussions/2905#discussioncomment-2596810
- https://stackoverflow.com/a/51302466/12814525
- https://github.com/epicweb-dev/web-forms
*/
export const widths = [640, 828, 1080, 1920];
export const defaultQuality = 75;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment