如何将Canvas数据保存到文件

49

我正在使用Node.js将Canvas保存为图像,通过在Canvas元素上使用toDataURL提取图像并在writeFile中进行操作,但它没有保存文件。以下是我的代码:

var fs = IMPORTS.require('fs');
var path = IMPORTS.require('path');
path.exists('images/', function(exists){
    if (exists) {

        fs.writeFile('images/icon.png', img, function(err){
            if (err) 
                callback({
                    error: false,
                    reply: err
                });
            console.log('Resized and saved in');
            callback({
                error: false,
                reply: 'success.'
            });
        });
    }
    else {
        callback({
            error: true,
            reply: 'File did not exist.'
        });
    }
 });    

你能否包含你的画布代码或测试网址,以便我们可以运行你的代码而不必编写任何样板文件?(请参见http://sscce.org/)如果你想要得到更多人的帮助,你可能还想详细说明一下你的描述。 - timoxley
2个回答

59

以下是在Nodejs中将画布数据保存到文件的一个具体示例。变量img是由canvas.toDataURL()生成的字符串。我假设您已经知道如何将该字符串从浏览器POST到您的Nodejs服务器。

生成所使用样例图片的HTML代码片段:

<canvas id="foo" width="20px" height="20px"></canvas>
<script>
var ctx = $('#foo')[0].getContext('2d');
for (var x = 0; x < 20; x += 10) {
    for (var y = 0; y < 20; y += 10) {
        if (x == y) { ctx.fillStyle = '#000000'; }
        else { ctx.fillStyle = '#8888aa'; }
        ctx.fillRect(x, y, 10, 10);
    }
}
console.log($('#foo')[0].toDataURL());
</script>

使用Node.js片段解码base64数据并保存图像:

const fs = require("fs").promises;

(async () => {
  
  // string generated by canvas.toDataURL()
  const img = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0"
      + "NAAAAKElEQVQ4jWNgYGD4Twzu6FhFFGYYNXDUwGFpIAk2E4dHDRw1cDgaCAASFOffhEIO"
      + "3gAAAABJRU5ErkJggg==";
  
  // strip off the data: url prefix to get just the base64-encoded bytes
  const data = img.replace(/^data:image\/\w+;base64,/, "");
  
  const buf = Buffer.from(data, "base64");
  await fs.writeFile("image.png", buf);
})();

输出:

在此输入图像描述


1
谢谢!我不知道我需要一个 Buffer,我只是尝试使用 base64.decode() 处理 base64 数据,但生成了一个损坏的文件。 - Husky
服务器代码示例对我有效。有人能解释一下为什么它不能处理这些数据吗:http://pastebin.com/raw.php?i=3yhGiQWR - user659025
我得到了缓冲区未定义。 - Krishna
2
对我来说,toDataUrl() 字符串中包含加号,而 body-parser 将其转换为空格字符放在了 request.body 中,因此我不得不再次将它们替换为加号,以避免我的 PNG 文件损坏:var data=img.slice(img.indexOf(',')+1).replace(/\s/g,'+'); - BeatriceThalo
4
根据Node.js官方文档,new Buffer已经被弃用。现在应该使用Buffer.from(data, 'base64')来代替。请注意不要改变原本的含义。 - Chris

22

我认为Canvas内部并不使用Base64存储数据,而是使用更高效的二进制格式存储数据。因此,将数据转换为base64再转回二进制PNG会非常缓慢且消耗内存。

通过快速查找node canvas文档,可以得到以下信息:

要创建一个PNGStream,请调用canvas.pngStream(),流将开始发出数据事件,并在完成时最终发出end事件。如果发生异常,则会发出错误事件。

var fs = require('fs')
  , out = fs.createWriteStream(__dirname + '/text.png')
  , stream = canvas.pngStream();

stream.on('data', function(chunk){
  out.write(chunk);
});

stream.on('end', function(){
  console.log('saved png');
});

目前仅支持同步流。如果您想异步执行,可以尝试使用 worker 或 cluster(我个人从未尝试过)。


console.log('saved png') 发生在文件保存之前。这是因为只支持同步吗?我该如何在文件保存后触发事件? - hamncheez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接