86 lines
2.2 KiB
TypeScript
86 lines
2.2 KiB
TypeScript
import { promises as fs } from 'fs'
|
|
import path from 'path'
|
|
|
|
export async function imageExists(imagePath: string): Promise<boolean> {
|
|
try {
|
|
const fullPath = path.join(process.cwd(), 'public', imagePath)
|
|
await fs.access(fullPath)
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
export async function getImageDimensions(
|
|
imagePath: string
|
|
): Promise<{ width: number; height: number } | null> {
|
|
try {
|
|
const fullPath = path.join(process.cwd(), 'public', imagePath)
|
|
const buffer = await fs.readFile(fullPath)
|
|
|
|
if (imagePath.endsWith('.png')) {
|
|
const width = buffer.readUInt32BE(16)
|
|
const height = buffer.readUInt32BE(20)
|
|
return { width, height }
|
|
}
|
|
|
|
if (imagePath.endsWith('.jpg') || imagePath.endsWith('.jpeg')) {
|
|
let offset = 2
|
|
while (offset < buffer.length) {
|
|
if (buffer[offset] !== 0xff) break
|
|
|
|
const marker = buffer[offset + 1]
|
|
if (marker === 0xc0 || marker === 0xc2) {
|
|
const height = buffer.readUInt16BE(offset + 5)
|
|
const width = buffer.readUInt16BE(offset + 7)
|
|
return { width, height }
|
|
}
|
|
|
|
offset += 2 + buffer.readUInt16BE(offset + 2)
|
|
}
|
|
}
|
|
|
|
return null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function getOptimizedImageUrl(
|
|
src: string,
|
|
width?: number,
|
|
height?: number,
|
|
quality: number = 75
|
|
): string {
|
|
const params = new URLSearchParams()
|
|
|
|
if (width) params.set('w', width.toString())
|
|
if (height) params.set('h', height.toString())
|
|
params.set('q', quality.toString())
|
|
|
|
const queryString = params.toString()
|
|
return queryString ? `${src}?${queryString}` : src
|
|
}
|
|
|
|
export async function getImageWithPlaceholder(
|
|
imagePath: string
|
|
): Promise<{ src: string; width: number; height: number; placeholder?: string }> {
|
|
const dimensions = await getImageDimensions(imagePath)
|
|
|
|
if (!dimensions) {
|
|
return {
|
|
src: imagePath,
|
|
width: 800,
|
|
height: 600,
|
|
}
|
|
}
|
|
|
|
const placeholder = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='${dimensions.width}' height='${dimensions.height}'%3E%3Crect width='${dimensions.width}' height='${dimensions.height}' fill='%2318181b'/%3E%3C/svg%3E`
|
|
|
|
return {
|
|
src: imagePath,
|
|
...dimensions,
|
|
placeholder,
|
|
}
|
|
}
|