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

如何在Node.js中重构canvas.toBlob()返回的PNG数据

How to reconstruct a valid PNG buffer in Node.js from Blob text obtained via Canvas.toBlob()?

I'm using Puppeteer to draw content on a browser Canvas, then trying to transfer the image data to Node.js context and save it as a PNG with OpenCV. The toDataURL() approach works fine, but when I use toBlob() and convert the Blob to a string for transfer, the reconstructed Buffer in Node.js isn't a valid PNG—OpenCV's imdecode can't create a Mat from it.

Here's my current code:

const puppeteer = require('puppeteer');
const cv = require('opencv4nodejs');

const pup = puppeteer.launch({ 
  headless: false, 
  args: [ 
    '--use-gl=swiftshader', 
    '--no-sandbox', 
    '--enable-surface-synchronization', 
    '--disable-web-security', 
  ] 
}).then(async browser => { 
  const page = (await browser.pages())[0]; 
  page.on('console', msg => console.log(msg.text())); 
  page.on("pageerror", function(err) { 
    const theTempValue = err.toString(); 
    console.log("Page error: " + theTempValue); 
  }); 
  page.on("error", function (err) { 
    const theTempValue = err.toString(); 
    console.log("Error: " + theTempValue); 
  }); 

  await page.exposeFunction('_setupCompleted', async () => { 
    await page.evaluate(async () => { 
      const c = document.getElementById("myCanvas"); 
      const dataUrl = c.toDataURL("image/png"); 
      const txt = await new Promise(resolve => c.toBlob((blb) => blb.text().then(resolve), "image/png")); 
      await window._saveMat(dataUrl, txt); 
    }); 
    await browser.close(); 
  }); 

  await page.exposeFunction('_saveMat', async (dataUrl, txt) => { 
    dataUrl = dataUrl.replace(/^data:image\/png;base64,/, ""); 
    const dataUrlBuffer = Buffer.from(dataUrl, 'base64'); 
    const dataUrlMat = cv.imdecode(dataUrlBuffer, -1); 
    cv.imwrite(`./dataUrl.png`, dataUrlMat); 

    const txtBuffer = Buffer.from(txt, 'utf8'); 
    const txtMat = cv.imdecode(txtBuffer, -1); 
    cv.imwrite(`./txtMat.png`, txtMat); 
  }); 

  await page.goto('http://127.0.0.1:8887/index.html', { waitUntil: 'networkidle2', }); 
});

The toDataURL flow works, but the toBlob -> text() -> Node.js Buffer flow produces an invalid PNG. How can I properly reconstruct a valid buffer from the Blob data in Node.js?


Solution: Avoid Blob.text()—Use Base64 Encoding for Binary Data

The core issue here is that Blob.text() decodes the binary PNG data as a UTF-8 string, which corrupts the data. PNG files contain byte sequences that aren't valid UTF-8 characters, so the decoder replaces invalid bytes with replacement characters (�), making the string impossible to convert back to the original binary buffer.

Instead, you should convert the Blob's raw binary data to a base64 string (just like toDataURL does internally) for transfer. Here's how to adjust your code:

Step 1: Modify the Browser-side Code to Convert Blob to Base64

Instead of using blb.text(), read the Blob as an ArrayBuffer, convert it to a Uint8Array, then encode that to a base64 string:

await page.evaluate(async () => {
  const c = document.getElementById("myCanvas");
  const dataUrl = c.toDataURL("image/png");
  
  // Convert Blob to base64 string instead of UTF-8 text
  const blobBase64 = await new Promise(resolve => {
    c.toBlob(async (blb) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve(reader.result.split(',')[1]); // Extract base64 part
      reader.readAsDataURL(blb);
    }, "image/png");
  });
  
  await window._saveMat(dataUrl, blobBase64);
});

Alternatively, if you prefer using ArrayBuffer directly:

const blobBase64 = await new Promise(resolve => {
  c.toBlob(async (blb) => {
    const buffer = await blb.arrayBuffer();
    const uint8Array = new Uint8Array(buffer);
    // Convert Uint8Array to base64 string
    const base64 = btoa(String.fromCharCode(...uint8Array));
    resolve(base64);
  }, "image/png");
});

Step 2: Update the Node-side Code to Decode the Base64 String

Your _saveMat function can now handle the Blob-derived base64 string the same way it handles the toDataURL result:

await page.exposeFunction('_saveMat', async (dataUrl, blobBase64) => {
  // Existing toDataURL handling stays the same
  dataUrl = dataUrl.replace(/^data:image\/png;base64,/, "");
  const dataUrlBuffer = Buffer.from(dataUrl, 'base64');
  const dataUrlMat = cv.imdecode(dataUrlBuffer, -1);
  cv.imwrite(`./dataUrl.png`, dataUrlMat);

  // Decode the Blob-derived base64 string to buffer
  const blobBuffer = Buffer.from(blobBase64, 'base64');
  const blobMat = cv.imdecode(blobBuffer, -1);
  cv.imwrite(`./blobMat.png`, blobMat);
});

Why This Works

Base64 encoding converts binary data into a safe, ASCII-compatible string that preserves all original byte information. Unlike UTF-8 decoding (used by Blob.text()), base64 doesn't alter any bytes during encoding or decoding, so you get an exact copy of the original PNG binary data in Node.js.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 23:27:38