중복 이미지 해시 재사용 추가

This commit is contained in:
2026-03-31 17:39:54 +09:00
parent d760c7331a
commit 4db1b21ad5
3 changed files with 118 additions and 7 deletions

View File

@@ -1,9 +1,12 @@
const fs = require('fs/promises')
const path = require('path')
const crypto = require('crypto')
const sharp = require('sharp')
const { nanoid } = require('nanoid')
const { findImageAssetByHash, createImageAsset } = require('../db')
const UPLOAD_ROOT = path.join(__dirname, '..', '..', 'uploads')
const OPTIMIZED_DIR = 'assets'
function ensureImageMimeType(file) {
return typeof file?.mimetype === 'string' && file.mimetype.startsWith('image/')
@@ -54,18 +57,75 @@ async function writeOptimizedImage({
.webp({ quality })
.toBuffer({ resolveWithObject: true })
const contentHash = crypto.createHash('sha256').update(data).digest('hex')
const existing = await findImageAssetByHash(contentHash)
if (existing) {
return {
src: existing.src,
size: existing.byteSize,
originalSize: existing.originalByteSize,
width: existing.width,
height: existing.height,
contentHash: existing.contentHash,
reused: true,
directory,
}
}
const filename = String(Date.now()) + '-' + nanoid() + '.webp'
const absoluteDir = path.join(UPLOAD_ROOT, directory)
const absoluteDir = path.join(UPLOAD_ROOT, OPTIMIZED_DIR)
const absolutePath = path.join(absoluteDir, filename)
const src = '/uploads/' + OPTIMIZED_DIR + '/' + filename
await fs.mkdir(absoluteDir, { recursive: true })
await fs.writeFile(absolutePath, data)
return {
src: '/uploads/' + directory + '/' + filename,
size: data.length,
originalSize: file.size || file.buffer.length,
width: info.width || 0,
height: info.height || 0,
try {
const asset = await createImageAsset({
id: nanoid(),
contentHash,
src,
mimeType: 'image/webp',
byteSize: data.length,
originalByteSize: file.size || file.buffer.length,
width: info.width || 0,
height: info.height || 0,
})
return {
src: asset.src,
size: asset.byteSize,
originalSize: asset.originalByteSize,
width: asset.width,
height: asset.height,
contentHash: asset.contentHash,
reused: false,
directory,
}
} catch (error) {
try {
await fs.unlink(absolutePath)
} catch (unlinkError) {
if (unlinkError?.code !== 'ENOENT') throw unlinkError
}
if (error?.code === 'ER_DUP_ENTRY') {
const asset = await findImageAssetByHash(contentHash)
if (asset) {
return {
src: asset.src,
size: asset.byteSize,
originalSize: asset.originalByteSize,
width: asset.width,
height: asset.height,
contentHash: asset.contentHash,
reused: true,
directory,
}
}
}
throw error
}
}