Last active
September 18, 2024 19:34
-
-
Save lambdaxyzt/1ad05e98b73aab71fc7714d150a1480a to your computer and use it in GitHub Desktop.
Remix Image Component ( use cache 'cacashe library' , stream base , sharp library )
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
*/ | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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