dedup generated images

main
Ashelyn Dawn 5 months ago
parent c74fad1793
commit 5289646abb
No known key found for this signature in database
GPG Key ID: D1980B8C6F349BC1

@ -1,4 +1,5 @@
import { promises as fs } from 'fs'
import { createHash } from 'node:crypto'
import path from 'path'
import { ImgHTMLAttributes } from 'react'
import sharp from 'sharp'
@ -24,77 +25,89 @@ type ImageProps = ImageAttrs
interface ProcessedImage {
hostPath: string,
randPrefix: string,
widths: {[width: number] : string}
origSrc: string,
aspectRatio: number,
}
async function ImageServer({src,...imgAttrs}: ImageProps) {
const srcImg = sharp(src)
const extension = path.extname(src)
const filename = path.basename(src, extension)
const randPrefix = (Math.random() + 1).toString(36).substring(7)
const width = 'width' in imgAttrs ? imgAttrs.width : null
const height = 'height' in imgAttrs ? imgAttrs.height : null
console.log(`processing ${src} for width ${width} and height ${height}`)
// Make sure we can write to the dir
await fs.mkdir(outputDir, {recursive: true})
// Handle where we have an array of dimensions
if (Array.isArray(width) || Array.isArray(height)) {
const dimension = Array.isArray(width) ? 'width' : 'height'
const sizes = Array.isArray(width) ? width : height as number[]
const suffix = dimension.charAt(0)
// Copy in original
fs.copyFile(src, path.join(outputDir, `${randPrefix}-${filename}${extension}`))
// Create resized copies
const srcSetLines = await Promise.all(sizes
.map(async size => {
const outputName = `${randPrefix}-${filename}-${size}${suffix}${extension}`
const outputPath = path.join(outputDir, outputName)
return srcImg
.resize({[dimension]: size})
.toFile(outputPath)
.then(result => `${serverPath}${outputName} ${result.width}w`)
}))
// Handle where we have an array of widths
if (Array.isArray(width)) {
let _src : string = src
const srcSet = (await Promise.all(width.map(async width => {
const {outputPath, origSrc} = await processImage(src, {width})
_src = origSrc
return `${outputPath} ${width}w`
}))).join(',')
return (
<img
src={src}
srcSet={srcSetLines.join(',')}
src={_src}
srcSet={srcSet}
{...imgAttrs}
width={undefined}
height={undefined}
/>
)
} else {
const dimensions = {
width: typeof width === 'number' ? width : undefined,
height: typeof height === 'number' ? height : undefined
}
const dimensionString = (() => {
if (dimensions.height && dimensions.height)
return `${dimensions.width}x${dimensions.height}`
if (dimensions.width)
return `${dimensions.width}w`
return `${dimensions.height}h`
})()
const outputName = `${randPrefix}-${filename}-${dimensionString}${extension}`
const outputPath = path.join(outputDir, outputName)
const outputSrc = serverPath + outputName
await srcImg.resize(dimensions).toFile(outputPath)
}else if (Array.isArray(height)) {
let _src : string = src
const srcSet = (await Promise.all(height.map(async height => {
const {outputPath, origSrc} = await processImage(src, {height})
_src = origSrc
return `${outputPath} ${width}w`
}))).join(',')
return (
<img
src={outputSrc}
src={_src}
srcSet={srcSet}
{...imgAttrs}
{...dimensions}
width={undefined}
height={undefined}
/>
)
} else {
const {outputPath, resolvedWidth} = await processImage(src, {width, height})
return <img src={outputPath} {...imgAttrs} height={undefined} width={resolvedWidth} />
}
}
async function processImage(sourcePath: string, {width, height} : {width?: number, height?: number}) : Promise<{outputPath: string, resolvedWidth: number, origSrc: string}> {
await fs.mkdir(outputDir, {recursive: true})
const hostPath = path.join(process.cwd(), sourcePath)
const srcImg = sharp(hostPath)
const metadata = await srcImg.metadata()
const extension = path.extname(hostPath)
const filename = path.basename(hostPath, extension)
const hashPrefix = createHash('sha256').update(hostPath).digest('hex').substr(0, 7)
const origSrc = `${hashPrefix}-${filename}${extension}`
const aspectRatio = metadata.width / metadata.height
const resolvedWidth = width ? width : aspectRatio * height
const outputName = `${hashPrefix}-${filename}-${resolvedWidth}w${extension}`
const origCopyStat = await fs.stat(path.join(outputDir, origSrc)).catch(() => undefined)
if (!origCopyStat?.isFile()) {
console.log(`copying file ${hostPath}`)
await fs.copyFile(hostPath, path.join(outputDir, origSrc))
}
const outputStat = await fs.stat(path.join(outputDir, outputName)).catch(() => undefined)
if (!outputStat?.isFile()) {
console.log(`resizing file ${hostPath} for width ${resolvedWidth}`)
await srcImg
.resize({width: resolvedWidth})
.toFile(path.join(outputDir, outputName))
}
return {
outputPath: serverPath + outputName,
resolvedWidth,
origSrc,
}
}

Loading…
Cancel
Save