feat(images): add sha256-deduplicated image downloader
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
40
src/lib/server/images/image-downloader.ts
Normal file
40
src/lib/server/images/image-downloader.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { fetchBuffer } from '../http';
|
||||
|
||||
const EXT_BY_CONTENT_TYPE: Record<string, string> = {
|
||||
'image/jpeg': '.jpg',
|
||||
'image/jpg': '.jpg',
|
||||
'image/png': '.png',
|
||||
'image/webp': '.webp',
|
||||
'image/gif': '.gif',
|
||||
'image/avif': '.avif'
|
||||
};
|
||||
|
||||
function extensionFor(contentType: string | null): string {
|
||||
if (!contentType) return '.bin';
|
||||
const base = contentType.split(';')[0].trim().toLowerCase();
|
||||
return EXT_BY_CONTENT_TYPE[base] ?? '.bin';
|
||||
}
|
||||
|
||||
export async function downloadImage(
|
||||
url: string,
|
||||
targetDir: string
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const { data, contentType } = await fetchBuffer(url, { maxBytes: 10 * 1024 * 1024 });
|
||||
const hash = createHash('sha256').update(data).digest('hex');
|
||||
const ext = extensionFor(contentType);
|
||||
const filename = `${hash}${ext}`;
|
||||
const target = join(targetDir, filename);
|
||||
if (!existsSync(target)) {
|
||||
await mkdir(targetDir, { recursive: true });
|
||||
await writeFile(target, data);
|
||||
}
|
||||
return filename;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user