新增图片批量处理
This commit is contained in:
138
lib/plugins/image/service.js
Normal file
138
lib/plugins/image/service.js
Normal file
@ -0,0 +1,138 @@
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user