Files
blog-press/docs/Web/Others/ImageProcessor.md

5.4 KiB
Raw Blame History

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();
}

  核心方法:resizejpegtoBuffer

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请求对象常用用法

  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. 返回JSONres.json({ success: true, data: {} });
  3. 设置状态码:res.status(404).json({ msg: 'Not Found' });
  4. 设置响应头:res.set('Content-Type', 'text/plain');
  5. 结束响应:res.end() :::