You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JIMP生成图片截断 并发请求返回残缺旧图报ERR_CONTENT_LENGTH_MISMATCH

问题根因
  • 所有请求生成图片时复用固定输出文件名:休闲模板统一写入aCasualInvite.png,正式模板统一写入aFancyInvite.png,并发场景下多个请求会同时读写同一份物理文件。
  • 当请求A正在往目标路径写图片内容、还没写完时,请求B可能已经开始读取这个半完成的文件返回给前端,或是直接覆盖请求A正在写入的文件,直接导致返回旧图片、残缺图片的问题。
  • 你碰到的Failed to load resource: net::ERR_CONTENT_LENGTH_MISMATCH报错也和这个问题直接相关:Express的sendFile会先读取文件元信息设置Content-Length响应头,如果读取文件过程中文件被其他请求改写、或是读到了只写入一半的文件,实际返回的内容长度和响应头标注的长度不匹配,就会触发这个浏览器报错。
  • 你代码里JIMP相关的await调用写法本身没有问题,和JIMP异步渲染逻辑无关,问题完全来自固定文件名导致的并发文件读写冲突。
修复方案
  • 方案1(最小改动):给每个请求生成唯一的临时文件名,避免多请求操作同一路径文件。可以用时间戳+随机字符串生成唯一标识,文件发送完成后定时清理避免占用磁盘,核心修改示例:
const fs = require('fs/promises');
// makeImg函数内生成输出路径部分替换原有逻辑
const uniqueSuffix = `${Date.now()}_${Math.random().toString(36).slice(2,8)}`;
const outputFileName = `invite_${uniqueSuffix}_${filename}.png`;
const outputFullPath = __dirname + '/' + outputFileName;
await image.writeAsync(outputFullPath);
// 1分钟后自动删除临时文件,避免磁盘空间被占满
setTimeout(() => fs.unlink(outputFullPath).catch(()=>{}), 60000);
return outputFileName;
  • 方案2(推荐,彻底规避文件冲突):跳过本地磁盘写入逻辑,JIMP处理完图片后直接输出Buffer返回给前端、传给NodeMailer做附件,完全消除文件IO带来的并发问题,也不需要额外清理临时文件。核心修改示例:
// 路由部分修改
app.post('/invite/:email/:name', async (req,res, next) => {
  try {
    const email = atob(req.params.email);
    const name = atob(req.params.name);
    const data = req.body;
    // 直接拿到图片二进制Buffer
    const inviteImageBuffer = await makeImg(name, data);
    res.type('png');
    res.send(inviteImageBuffer);
    // 发邮件时直接把这个Buffer传给nodemailer的attachments字段即可,不需要写本地文件
  } catch (error) {
    return next(error);
  }
});

// makeImg函数末尾替换原有writeAsync逻辑
// ...前面的模板读取、文字打印逻辑保持不变
// 直接返回PNG格式的Buffer,不写本地文件
return image.getBufferAsync(Jimp.MIME_PNG);

内容的提问来源于stack exchange,提问作者Loom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:21:40