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

Axios调用weasyprint-rest生成PDF重复请求出现socket hang up错误

解决WeasyPrint REST API二次请求出现ECONNRESET问题

针对你遇到的首次请求正常、二次及后续请求触发ECONNRESET(socket hang up)的问题,结合你已尝试的超时配置、KeepAlive Agent方案,下面给出几个针对性的解决思路:

1. 每次请求创建全新的FormData和流实例

核心问题大概率是复用了已被读取完毕的流对象:第一次请求时fs.createReadStream生成的流已被消费,二次请求复用同一FormData或流时,没有数据可发送,服务器会主动断开连接。

修改代码,将FormData创建、流的append逻辑放到每次请求的函数内部,确保每次请求都是全新实例:

const axios = require('axios');
const FormData = require("form-data");
const fs = require('fs');
const path = require('path');

async function generatePdf() {
  // 每次请求都初始化新的FormData和流
  const data = new FormData();
  data.append("html", fs.createReadStream(path.join(__dirname, "../assets/html/report.html")));
  data.append('asset[]', fs.createReadStream(path.join(__dirname, `../assets/images/footerLogo.png`)));
  data.append("style", fs.createReadStream(path.join(__dirname, "../assets/css/report.css")));

  const configConvert = {
    url: process.env.HTMLTOPDF_URL,
    headers: { ...data.getHeaders() },
    responseType: "arraybuffer",
    method: "post",
    timeout: 30000,
    data: data
  };

  try {
    const pdfResult = await axios(configConvert);
    console.log(pdfResult.data);
    return pdfResult.data;
  } catch (err) {
    console.error(err);
  }
}

2. 显式销毁流,避免资源泄漏

如果流未被正确清理,可能导致连接资源泄漏。可以在请求完成后(无论成功失败)手动销毁所有流对象:

async function generatePdf() {
  // 单独声明流对象,方便后续销毁
  const htmlStream = fs.createReadStream(path.join(__dirname, "../assets/html/report.html"));
  const logoStream = fs.createReadStream(path.join(__dirname, `../assets/images/footerLogo.png`));
  const cssStream = fs.createReadStream(path.join(__dirname, "../assets/css/report.css"));

  const data = new FormData();
  data.append("html", htmlStream);
  data.append('asset[]', logoStream);
  data.append("style", cssStream);

  const configConvert = {
    url: process.env.HTMLTOPDF_URL,
    headers: { ...data.getHeaders() },
    responseType: "arraybuffer",
    method: "post",
    timeout: 30000,
    data: data
  };

  try {
    const pdfResult = await axios(configConvert);
    console.log(pdfResult.data);
    return pdfResult.data;
  } catch (err) {
    console.error(err);
  } finally {
    // 手动销毁流,释放底层资源
    htmlStream.destroy();
    logoStream.destroy();
    cssStream.destroy();
  }
}

3. 完善KeepAlive Agent的配置

之前的KeepAlive配置参数不全,试试补充maxSockets和keepAliveMsecs参数,优化连接复用逻辑:

const https = require('https');
const axios = require('axios');
const FormData = require("form-data");
const fs = require('fs');
const path = require('path');

// 创建配置更完整的KeepAlive Agent
const httpsAgent = new https.Agent({
  keepAlive: true,
  maxSockets: Infinity, // 不限制并发连接数
  keepAliveMsecs: 30000, // 连接保持时长30秒
});

async function generatePdf() {
  const data = new FormData();
  data.append("html", fs.createReadStream(path.join(__dirname, "../assets/html/report.html")));
  data.append('asset[]', fs.createReadStream(path.join(__dirname, `../assets/images/footerLogo.png`)));
  data.append("style", fs.createReadStream(path.join(__dirname, "../assets/css/report.css")));

  const configConvert = {
    url: process.env.HTMLTOPDF_URL,
    headers: { ...data.getHeaders() },
    responseType: "arraybuffer",
    method: "post",
    timeout: 30000,
    httpsAgent: httpsAgent,
    data: data
  };

  try {
    const pdfResult = await axios(configConvert);
    console.log(pdfResult.data);
    return pdfResult.data;
  } catch (err) {
    console.error(err);
  }
}

4. 换用Node.js原生fetch API测试

如果以上方案都无效,试试用Node.js原生fetch替代Axios,排除Axios自身处理逻辑的问题:

const fs = require('fs');
const path = require('path');
const FormData = require("form-data");

async function generatePdfWithFetch() {
  const data = new FormData();
  data.append("html", fs.createReadStream(path.join(__dirname, "../assets/html/report.html")));
  data.append('asset[]', fs.createReadStream(path.join(__dirname, `../assets/images/footerLogo.png`)));
  data.append("style", fs.createReadStream(path.join(__dirname, "../assets/css/report.css")));

  try {
    const response = await fetch(process.env.HTMLTOPDF_URL, {
      method: 'POST',
      headers: data.getHeaders(),
      body: data,
      signal: AbortSignal.timeout(30000) // 设置请求超时
    });

    if (!response.ok) {
      throw new Error(`请求失败!状态码:${response.status}`);
    }

    const pdfBuffer = await response.arrayBuffer();
    console.log(pdfBuffer);
    return pdfBuffer;
  } catch (err) {
    console.error(err);
  }
}

为什么Postman可以正常请求?

Postman每次请求都是独立的上下文,会自动创建全新的请求体和流对象,不会复用之前的资源,因此不会遇到流已被消费的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 15:50:18