maplestory/backend/services/image.js
caadiq c072cccf44 이미지 서비스 통합: boss-crystal/image.js 제거 + safeDelete 헬퍼 추가
- services/boss-crystal/image.js의 uploadBossImage/deleteBossImage는
  services/image.js의 convertAndUploadTo와 safeDelete로 대체 가능해서 제거
- services/image.js에 safeDelete(path) 헬퍼 추가 (삭제 실패해도 흐름을
  끊지 않고 warn 로그). 기존에 try/catch 인라인으로 흩어져있던 세 곳 통일
- routes/admin/boss-crystal.js에 BOSS_IMAGE_PREFIX 상수 인라인

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:33:58 +09:00

52 lines
1.6 KiB
JavaScript

import sharp from 'sharp';
import crypto from 'crypto';
import { uploadObject, deleteObject } from '../lib/s3.js';
/**
* 이미지를 webp로 변환하고 RustFS에 업로드
* @param {Buffer} buffer - 원본 이미지 버퍼
* @returns {Promise<{path: string, width: number, height: number, size: number}>}
*/
export async function convertAndUpload(buffer) {
const webpBuffer = await sharp(buffer)
.webp({ quality: 90 })
.toBuffer();
const metadata = await sharp(webpBuffer).metadata();
const hash = crypto.createHash('sha256').update(webpBuffer).digest('hex').slice(0, 16);
const path = `common/${hash}.webp`;
await uploadObject(path, webpBuffer, 'image/webp');
return {
path,
width: metadata.width,
height: metadata.height,
size: webpBuffer.length,
};
}
export async function deleteFromS3(path) {
await deleteObject(path);
}
// 삭제 실패해도 흐름을 끊지 않는 버전 (이전 이미지 정리 등에 사용)
export async function safeDelete(path) {
if (!path) return;
try {
await deleteObject(path);
} catch (err) {
console.warn(`S3 삭제 실패 (${path}):`, err.message);
}
}
/**
* 지정한 경로로 webp 변환 후 업로드 (덮어쓰기)
* @param {Buffer} buffer - 원본 이미지 버퍼
* @param {string} path - S3 키 (확장자 포함). 예: 'symbol/아케인심볼(소멸의 여로).webp'
*/
export async function convertAndUploadTo(buffer, path) {
const webpBuffer = await sharp(buffer).webp({ quality: 90 }).toBuffer();
await uploadObject(path, webpBuffer, 'image/webp');
return { path, size: webpBuffer.length };
}