5.4 KiB
5.4 KiB
title, date
| title | date |
|---|---|
| 图片处理服务 | 2026-06-04 |
一、简介
基于 Node.js + Sharp 实现的轻量级图片处理服务。
sharp是 Node.js 下非常常用的高性能图像处理库,主要用来缩放、裁剪、旋转、格式转换、压缩图片等,速度快、内存占用低。
支持路径+参数的形式实时压缩图片,例如ip:port/a.png?q=80&w=1024&h=768
二、核心方法
2.1 根据参数调整图片
const sharp = require('sharp');
// 压缩配置
const compressOptions = {
quality: 0.7,
maxHeight: 800,
maxWidth: 600
};
/**
* 处理图片压缩和调整大小
* @param {string} imagePath 图片路径
* @param {Object} query 查询参数
* @returns {Promise<Buffer>} 处理后的图片Buffer
*/
async function processImage(imagePath, query) {
let sharpInstance = sharp(imagePath).rotate();
// 处理宽高参数
let targetWidth = parseInt(query.w) || parseInt(query.width);
let targetHeight = parseInt(query.h) || parseInt(query.height);
// 如果没有指定宽高,使用配置的默认最大宽高
if (!targetWidth && !targetHeight) {
targetWidth = compressOptions.maxWidth;
targetHeight = compressOptions.maxHeight;
}
// 如果指定了宽高,进行调整大小处理
if (targetWidth || targetHeight) {
const resizeOptions = {
width: targetWidth,
height: targetHeight,
fit: sharp.fit.inside, // 保持宽高比,图片会完整显示在指定区域内
withoutEnlargement: true // 不放大比原始尺寸小的图片
};
sharpInstance = sharpInstance.resize(resizeOptions);
}
// 处理质量参数(优先使用查询参数,否则使用配置的默认值)
const quality = query.q ? Math.max(1, Math.min(100, parseInt(query.q))) :
Math.round(compressOptions.quality * 100);
const finalQuality = Math.max(1, Math.min(100, quality));
// 转换为JPEG格式并压缩
return sharpInstance.jpeg({quality: finalQuality, mozjpeg: true}).toBuffer();
}
2.2 返回图片数据
/**
* 发送响应
* @param {ServerResponse} res 响应对象
* @param {number} statusCode 状态码
* @param {string} message 信息
*/
function sendResponse(res, statusCode, message) {
res.writeHead(statusCode, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(message);
}
const server = http.createServer(async (req, res) => {
if (req.method !== 'GET') {
return sendResponse(res, 405, '仅支持GET请求');
}
try {
const parsedUrl = url.parse(req.url, true);
const requestPath = parsedUrl.pathname;
const query = parsedUrl.query;
// 过滤根路径请求(避免直接访问服务根目录)
if (requestPath === '/') {
return sendResponse(res, 200, '图片处理服务已启动!');
}
const requestedFilePath = requestPath.slice(1);
const fullImagePath = path.join(IMAGE_BASE_DIR, requestedFilePath);
// 安全检查:防止路径遍历
if (!isSafePath(fullImagePath, IMAGE_BASE_DIR)) {
return sendResponse(res, 403, '访问拒绝:非法路径(路径遍历攻击)');
}
if (!fs.existsSync(fullImagePath)) {
return sendResponse(res, 404, `图片不存在:${requestedFilePath}`);
}
// 如果没有压缩参数,直接返回原图
if (!query.q && !query.w && !query.width && !query.h && !query.height) {
const fileStream = fs.createReadStream(fullImagePath);
const ext = path.extname(fullImagePath).slice(1);
res.writeHead(200, {'Content-Type': `image/${ext}`});
// 流式传输文件(适合大文件)
fileStream.pipe(res);
fileStream.on('error', (err) => {
sendResponse(res, 500, `读取文件失败:${err.message}`);
});
return;
}
// 处理图片压缩和调整大小
const imageBuffer = await processImage(fullImagePath, query);
res.writeHead(200, {
'Content-Type': 'image/jpeg',
'Content-Length': imageBuffer.length,
'Cache-Control': 'public, max-age=86400' // 缓存1天
});
res.end(imageBuffer);
} catch (error) {
console.error('服务器异常:', error);
sendResponse(res, 500, `服务器内部错误:${error.message}`);
}
});
原生Node.js中res用法:
res.writeHead(statusCode, [statusMessage], [headers]);
res.end(message)
::: tip Express中req(请求对象)常用用法:
- 获取请求路径参数:
/user/:id->req.params.id - 获取查询参数(?key=value):
/search?name=tom&age=18->req.query.name - 获取请求体:
req.body.username - 获取请求头:
req.headers['user-agent']
Express中res(响应对象)常用用法:
- 返回普通文本:
res.send('Hello Express'); - 返回JSON:
res.json({ success: true, data: {} }); - 设置状态码:
res.status(404).json({ msg: 'Not Found' }); - 设置响应头:
res.set('Content-Type', 'text/plain'); - 结束响应:
res.end():::