feat:增加SweetHub项目文档
This commit is contained in:
154
docs/Web/Others/ImageProcessor.md
Normal file
154
docs/Web/Others/ImageProcessor.md
Normal file
@@ -0,0 +1,154 @@
|
||||
---
|
||||
title: 图片处理服务
|
||||
date: 2026-06-04
|
||||
---
|
||||
|
||||
# 一、简介
|
||||
  基于 Node.js + Sharp 实现的轻量级图片处理服务。
|
||||
  [sharp](https://sharp.pixelplumbing.com/)是 Node.js 下非常常用的高性能图像处理库,主要用来缩放、裁剪、旋转、格式转换、压缩图片等,速度快、内存占用低。
|
||||
  支持路径+参数的形式实时压缩图片,例如`ip:port/a.png?q=80&w=1024&h=768`
|
||||
|
||||
# 二、核心方法
|
||||
## 2.1 根据参数调整图片
|
||||
```JavaScript
|
||||
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();
|
||||
}
|
||||
```
|
||||
|
||||
  核心方法:[resize](https://sharp.nodejs.cn/api-resize/)、[jpeg](https://sharp.nodejs.cn/api-output/#jpeg)、[toBuffer](https://sharp.nodejs.cn/api-output/#tobuffer)。
|
||||
|
||||
## 2.2 返回图片数据
|
||||
```JavaScript
|
||||
/**
|
||||
* 发送响应
|
||||
* @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用法:
|
||||
```js
|
||||
res.writeHead(statusCode, [statusMessage], [headers]);
|
||||
res.end(message)
|
||||
```
|
||||
|
||||
::: tip
|
||||
Express中req(请求对象)常用用法:
|
||||
1. 获取请求路径参数:`/user/:id`->`req.params.id`
|
||||
2. 获取查询参数(?key=value):`/search?name=tom&age=18`->`req.query.name`
|
||||
3. 获取请求体:`req.body.username`
|
||||
4. 获取请求头:`req.headers['user-agent']`
|
||||
|
||||
Express中res(响应对象)常用用法:
|
||||
1. 返回普通文本:`res.send('Hello Express');`
|
||||
2. 返回JSON:`res.json({ success: true, data: {} });`
|
||||
3. 设置状态码:`res.status(404).json({ msg: 'Not Found' });`
|
||||
4. 设置响应头:`res.set('Content-Type', 'text/plain');`
|
||||
5. 结束响应:`res.end()`
|
||||
:::
|
||||
Reference in New Issue
Block a user