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

使用Node.js的Sharp转换含外部图片的SVG为PNG遇内存错误

解决Sharp转换含外部图片的SVG到PNG时的内存分配失败问题

问题背景

在Node.js环境下使用Sharp库将包含外部图片链接的SVG转换为PNG时,尝试通过将外部图片转为Base64嵌入SVG的方式处理,但因Base64字符串过长,Sharp抛出内存分配失败的XML解析错误,即使压缩图片也无法解决。

原代码

export const convertSvgToPng = async (svgCode: string) => {
  try {
    // Convert the SVG code to a PNG buffer
    const pngBuffer = await embedImagesInSvg(svgCode);

    return pngBuffer;
  } catch (error) {
    console.error(`Error converting SVG to PNG:`, error);
  }
};

// Utility function to download image and convert to Base64
async function fetchImageAsBase64(url: string) {
  const response = await axios.get(url, { responseType: 'arraybuffer' });
  // Compress the image using sharp
  const image = sharp(response.data);

  const compressedImageBuffer = await image.png({ compressionLevel: 9 }).toBuffer();
  const base64Image = compressedImageBuffer.toString('base64');
  const mimeType = response.headers['content-type'];
  return `data:${mimeType};base64,${base64Image}`;
}


// Function to modify SVG by embedding multiple base64 images
async function embedImagesInSvg(svgCode: string) {
  // let svgContent = fs.readFileSync(svgFilePath, 'utf8');

  // Regex to match all xlink:href occurrences
  const regex = /xlink:href="([^"]+)"/g;
  let match;
  const imagePromises = [];

  // Collect all image URLs
  while ((match = regex.exec(svgCode)) !== null) {
    const imageUrl = match[1];
    imagePromises.push(fetchImageAsBase64(imageUrl));
  }

  try {
    // Wait for all images to be fetched and converted to base64
    const base64Images = await Promise.all(imagePromises);

    // Replace each xlink:href with the base64-encoded image
    svgCode = svgCode.replace(regex, () => {
      const base64Image = base64Images.shift();
      return `xlink:href="${base64Image}"`;
    });

    // Convert the modified SVG to PNG using sharp
    return sharp(Buffer.from(svgCode)).png().toBuffer();
  } catch (error) {
    console.error('Error fetching one or more external images:', error);
  }
}

报错信息

Error converting SVG to PNG: Error: Input buffer has corrupt header: glib: XML parse error: fatal error code=2 (1) in (null):1:10003752: Memory allocation failed : Huge input lookup

解决方案

1. 放弃Base64嵌入,改用本地临时文件引用

Base64会让SVG体积暴增,触发XML解析时的内存限制。改为将外部图片下载到本地临时文件,修改SVG的图片链接指向本地路径,让Sharp直接解析带本地路径的SVG,避免内存过载。

先安装临时文件管理库:

npm install tmp

2. 修改后的完整代码

import sharp from 'sharp';
import axios from 'axios';
import tmp from 'tmp';
import fs from 'fs/promises';

// 配置临时文件自动清理
tmp.setGracefulCleanup();

export const convertSvgToPng = async (svgCode: string) => {
  try {
    const pngBuffer = await embedImagesInSvg(svgCode);
    return pngBuffer;
  } catch (error) {
    console.error(`Error converting SVG to PNG:`, error);
    throw error;
  }
};

// 下载图片到临时文件并返回文件路径
async function fetchImageToTempFile(url: string) {
  const response = await axios.get(url, { responseType: 'arraybuffer' });
  
  // 创建临时文件
  const tempFile = tmp.fileSync({ postfix: '.png' });
  await fs.writeFile(tempFile.name, response.data);
  
  // 压缩图片(可选,根据需求保留)
  await sharp(tempFile.name)
    .png({ compressionLevel: 9 })
    .toFile(tempFile.name);
  
  return tempFile;
}

async function embedImagesInSvg(svgCode: string) {
  const regex = /xlink:href="([^"]+)"/g;
  const matches = [...svgCode.matchAll(regex)];
  const tempFiles: tmp.FileResult[] = [];

  try {
    // 下载所有图片到临时文件
    const imagePaths = await Promise.all(
      matches.map(async (match) => {
        const tempFile = await fetchImageToTempFile(match[1]);
        tempFiles.push(tempFile);
        return tempFile.name;
      })
    );

    // 替换SVG中的图片链接为本地临时文件路径
    let modifiedSvg = svgCode;
    matches.forEach((match, index) => {
      modifiedSvg = modifiedSvg.replace(
        match[0],
        `xlink:href="${imagePaths[index]}"`
      );
    });

    // 转换SVG到PNG
    return sharp(Buffer.from(modifiedSvg)).png().toBuffer();
  } finally {
    // 清理所有临时文件
    tempFiles.forEach(file => file.removeCallback());
  }
}

关键优化点

  • 避免Base64膨胀:用本地文件路径替代Base64字符串,大幅减少SVG体积,绕过XML解析的内存限制。
  • 可靠的匹配替换:用matchAll收集所有匹配项,再逐个替换,避免原正则全局替换时的索引异常。
  • 自动清理临时文件:利用tmp库的自动清理机制,防止临时文件堆积。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:40:14