139 lines
4.5 KiB
JavaScript
139 lines
4.5 KiB
JavaScript
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}`);
|
|
}
|
|
}
|