You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

124 lines
4.7 KiB

const sharp = require('sharp');
class ImageProcessor {
/**
* 为图片添加文字水印
* @param {Buffer} imageBuffer - 原始图片的Buffer数据
* @param {String} text - 水印文字内容
* @param {Object} options - 水印配置选项
* @returns {Promise<Buffer>} - 添加水印后的图片Buffer
*/
static async addWatermark(imageBuffer, text = '又鸟蛋平台', options = {}) {
try {
console.log('【图片处理】开始添加水印');
// 设置默认配置
const defaultOptions = {
fontSize: 20, // 字体大小 - 减小以确保完整显示
color: 'rgba(0,0,0,0.5)', // 文字颜色(加深以便更清晰)
position: 'bottom-right', // 水印位置
marginX: -50, // X轴边距 - 调整使水印居中在红色框中
marginY: 10 // Y轴边距 - 减小使水印靠下,放入红色框中
};
// 强制使用'bottom-right'位置
options.position = 'bottom-right';
const config = { ...defaultOptions, ...options };
// 使用sharp处理图片
const image = sharp(imageBuffer);
// 获取图片信息以确定水印位置
const metadata = await image.metadata();
const width = metadata.width || 800;
const height = metadata.height || 600;
// 确定水印位置
let x = config.marginX;
let y = config.marginY;
if (config.position === 'bottom-right') {
// 右下角位置,需要计算文字宽度(这里简化处理,实际应该根据字体计算)
// 这里使用一个简单的估算:每个字符约占字体大小的0.6倍宽度
const estimatedTextWidth = text.length * config.fontSize * 0.6;
x = width - estimatedTextWidth - config.marginX;
y = height - config.fontSize - config.marginY;
} else if (config.position === 'center') {
x = (width / 2) - (text.length * config.fontSize * 0.3);
y = height / 2;
} else if (config.position === 'top-left') {
// 左上角,使用默认的margin值
}
// 确保位置不会超出图片边界
x = Math.max(0, Math.min(x, width - 1));
y = Math.max(config.fontSize, Math.min(y, height - 1));
// 添加文字水印
const watermarkedBuffer = await image
.composite([{
input: Buffer.from(`<svg width="${width}" height="${height}">
<text x="${x}" y="${y}" font-family="Arial" font-size="${config.fontSize}" fill="${config.color}">${text}</text>
</svg>`),
gravity: 'southeast'
}])
.toBuffer();
console.log('【图片处理】水印添加成功');
return watermarkedBuffer;
} catch (error) {
console.error('【图片处理】添加水印失败:', error.message);
console.error('【图片处理】错误详情:', error);
// 如果水印添加失败,返回原始图片
return imageBuffer;
}
}
/**
* 批量为图片添加水印
* @param {Array<Buffer>} imageBuffers - 图片Buffer数组
* @param {String} text - 水印文字内容
* @param {Object} options - 水印配置选项
* @returns {Promise<Array<Buffer>>} - 添加水印后的图片Buffer数组
*/
static async addWatermarkToMultiple(imageBuffers, text = '又鸟蛋平台', options = {}) {
try {
console.log(`【图片处理】开始批量添加水印,共${imageBuffers.length}张图片`);
const watermarkedPromises = imageBuffers.map(buffer =>
this.addWatermark(buffer, text, options)
);
const results = await Promise.all(watermarkedPromises);
console.log('【图片处理】批量水印添加完成');
return results;
} catch (error) {
console.error('【图片处理】批量添加水印失败:', error.message);
throw error;
}
}
/**
* 为Base64编码的图片添加水印
* @param {String} base64Image - Base64编码的图片
* @param {String} text - 水印文字内容
* @param {Object} options - 水印配置选项
* @returns {Promise<Buffer>} - 添加水印后的图片Buffer
*/
static async addWatermarkToBase64(base64Image, text = '又鸟蛋平台', options = {}) {
try {
// 移除Base64前缀
const base64Data = base64Image.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, '');
// 转换为Buffer
const buffer = Buffer.from(base64Data, 'base64');
// 添加水印
return await this.addWatermark(buffer, text, options);
} catch (error) {
console.error('【图片处理】为Base64图片添加水印失败:', error.message);
throw error;
}
}
}
module.exports = ImageProcessor;