// 校对本地已下载的文物图片:识别真实图片类型、规范扩展名,并把 DB image_url 指向本地路径。 import { Pool } from "pg"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const PUBLIC_DIR = path.resolve(__dirname, "../../apps/web/public/artifacts"); const pool = new Pool({ connectionString: process.env.DATABASE_URL ?? "postgresql://postgres@localhost:5432/wenwumap", }); function detectExt(buf) { if (buf.length < 12) return null; if (buf[0] === 0xff && buf[1] === 0xd8) return "jpg"; if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) return "png"; if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) return "gif"; if (buf.slice(0, 4).toString() === "RIFF" && buf.slice(8, 12).toString() === "WEBP") return "webp"; if (buf.slice(0, 5).toString().includes(" { if (!fs.existsSync(PUBLIC_DIR)) { console.log("无本地图片目录"); return; } const files = fs.readdirSync(PUBLIC_DIR); const { rows } = await pool.query( "SELECT id, image_url FROM artifacts WHERE image_url IS NOT NULL ORDER BY unified_map_id" ); let ok = 0; let bad = 0; for (const { id } of rows) { const match = files.find((f) => f.startsWith(id)); if (!match) continue; const full = path.join(PUBLIC_DIR, match); const stat = fs.statSync(full); if (stat.size < 1024) { fs.rmSync(full); bad++; continue; } const buf = fs.readFileSync(full).subarray(0, 16); const ext = detectExt(buf); if (!ext) { bad++; console.log(`✗ ${id} 非有效图片,跳过`); continue; } const desired = `${id}.${ext}`; if (match !== desired) { fs.renameSync(full, path.join(PUBLIC_DIR, desired)); // 清理同 id 其它残留 files .filter((f) => f.startsWith(id) && f !== desired && f !== match) .forEach((f) => fs.rmSync(path.join(PUBLIC_DIR, f), { force: true })); } await pool.query("UPDATE artifacts SET image_url = $1 WHERE id = $2", [ `/artifacts/${desired}`, id, ]); ok++; console.log(`✓ ${id} -> /artifacts/${desired}`); } await pool.end(); console.log(`校对完成:本地化 ${ok},无效 ${bad}`); }; main().catch((e) => { console.error(e); process.exit(1); });