import { Jimp } from "jimp"; import path from "path"; import fs from "fs"; export async function compressImage(inputPath, quality = 80) { const ext = path.extname(inputPath).toLowerCase(); const basename = path.basename(inputPath, ext); const outputDir = path.join(path.dirname(inputPath), "compressed"); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const outputPath = path.join(outputDir, `${basename}${ext}`); const image = await Jimp.read(inputPath); await image.write(outputPath, { quality }); return outputPath; } export async function resizeImage(inputPath, sizes = [{ width: 800 }, { width: 400 }, { width: 200 }]) { const ext = path.extname(inputPath).toLowerCase(); const basename = path.basename(inputPath, ext); const outputDir = path.join(path.dirname(inputPath), "resized"); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const outputs = []; for (const size of sizes) { const outputPath = path.join(outputDir, `${basename}_${size.width}w${ext}`); const image = await Jimp.read(inputPath); image.resize({ w: size.width }); await image.write(outputPath); outputs.push(outputPath); } return outputs; } export async function addWatermark(inputPath, watermarkPath, position = "southeast") { const ext = path.extname(inputPath).toLowerCase(); const basename = path.basename(inputPath, ext); const outputDir = path.join(path.dirname(inputPath), "watermarked"); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const outputPath = path.join(outputDir, `${basename}${ext}`); const image = await Jimp.read(inputPath); const watermark = await Jimp.read(watermarkPath); watermark.resize({ w: Math.floor(image.bitmap.width * 0.2) }); const positions = { northwest: { x: 10, y: 10 }, northeast: { x: image.bitmap.width - watermark.bitmap.width - 10, y: 10 }, southwest: { x: 10, y: image.bitmap.height - watermark.bitmap.height - 10 }, southeast: { x: image.bitmap.width - watermark.bitmap.width - 10, y: image.bitmap.height - watermark.bitmap.height - 10 } }; const pos = positions[position]; image.composite(watermark, pos.x, pos.y); await image.write(outputPath); return outputPath; } export async function unifyBackground(inputPath, bgColor = 0xFFFFFFFF) { const ext = path.extname(inputPath).toLowerCase(); const basename = path.basename(inputPath, ext); const outputDir = path.join(path.dirname(inputPath), "background"); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const outputPath = path.join(outputDir, `${basename}${ext}`); const image = await Jimp.read(inputPath); const bg = new Jimp({ width: image.bitmap.width, height: image.bitmap.height, color: bgColor }); bg.composite(image, 0, 0); await bg.write(outputPath); return outputPath; } export async function cropWithPadding(inputPath, targetWidth, targetHeight, bgColor = 0xFFFFFFFF) { const ext = path.extname(inputPath).toLowerCase(); const basename = path.basename(inputPath, ext); const outputDir = path.join(path.dirname(inputPath), "cropped"); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const outputPath = path.join(outputDir, `${basename}_${targetWidth}x${targetHeight}${ext}`); const image = await Jimp.read(inputPath); const aspectRatio = image.bitmap.width / image.bitmap.height; const targetRatio = targetWidth / targetHeight; if (aspectRatio > targetRatio) { image.resize({ w: targetWidth }); } else { image.resize({ h: targetHeight }); } const bg = new Jimp({ width: targetWidth, height: targetHeight, color: bgColor }); const x = Math.floor((targetWidth - image.bitmap.width) / 2); const y = Math.floor((targetHeight - image.bitmap.height) / 2); bg.composite(image, x, y); await bg.write(outputPath); return outputPath; } export async function processImage(file, operation, params) { switch (operation) { case "compress": return await compressImage(file, params.quality); case "resize": return await resizeImage(file, params.sizes); case "watermark": return await addWatermark(file, params.watermarkPath, params.position); case "background": return await unifyBackground(file, params.bgColor); case "crop": return await cropWithPadding(file, params.width, params.height, params.bgColor); default: throw new Error(`未知操作: ${operation}`); } }