// ============================================================ // thumbnail.ts — 缩略图生成工具函数 // // 职责: // - 使用 sharp 库生成图片缩略图 // - 内存优化配置 // // 依赖: // - sharp (libvips) - 可选依赖,用于高效图片处理 // ============================================================ import fs from 'node:fs'; import path from 'node:path'; import sharp from 'sharp'; import { ensureDir } from './path'; // ---------- sharp/libvips 内存优化 ---------- // 单块内存上限 128MB(默认 512MB,降低以适配 Electron 单进程内存压力) sharp.cache({ memory: 128, items: 100 }); // libvips 并发数 1(减少大图并发处理时的内存峰值) sharp.concurrency(1); /** * 生成图片缩略图(sharp 流式管线) * * 特性: * - shrinkOnLoad:PNG/JPEG/WebP/TIFF 解码器直接降采样,8000px 原图解码即降至 ~512px * - sequentialRead:顺序读取减少随机 I/O 内存开销 * - limitInputPixels:默认 268MP(16384²),覆盖 8K+ 素材 * * @param srcPath - 源文件路径 * @param destPath - 目标文件路径 * @param maxWidth - 最大宽度(默认 256) * @param quality - JPEG 质量(默认 60) * @returns 目标文件路径 */ export async function generateThumbnail( srcPath: string, destPath: string, maxWidth: number = 256, quality: number = 60, ): Promise { if (!fs.existsSync(srcPath)) { throw new Error('源文件不存在'); } ensureDir(path.dirname(destPath)); await sharp(srcPath, { animated: false, // 只取第一帧(GIF/APNG 等多帧格式) sequentialRead: true, limitInputPixels: 268402689, }) .resize({ width: maxWidth, withoutEnlargement: true, fit: 'inside' }) .jpeg({ quality, mozjpeg: true }) // mozjpeg 压缩率更优 .toFile(destPath); return destPath; }