【问题标题】:Server Side d3 - Encoding SVG as a Base64 Image服务器端 d3 - 将 SVG 编码为 Base64 图像
【发布时间】:2017-09-03 18:19:22
【问题描述】:

我正在尝试将 D3 图表编码为 base64 图像以用于 HTML 电子邮件

到目前为止我有:

var express = require('express');
var app = express();
var jsdom = require('jsdom');

app.get('/chart', function (request, response) {
    try {
        jsdom.env(
            "<html><body><svg id=\"svg\"></svg></body></html>",
            ['http://d3js.org/d3.v3.min.js'],
            function (err, window) {
                var svg = window.d3.select("svg")
                    .attr("width", 100)
                    .attr("height", 100);

                svg.append("rect")
                    .attr("x", 10)
                    .attr("y", 10)
                    .attr("width", 80)
                    .attr("height", 80)
                    .style("fill", "orange");

                var encoded = ...; // How do I now encode this?

                response.send({
                    "html": window.d3.select("body").html(),
                    "encoded": encoded
                });
            }
        );
    } catch (err) {
        console.error(err);
    }
});

// Run Server
var port = process.env.PORT || 8305;
var server = app.listen(port, function () {
    var host = server.address().address;
    console.log('App listening at http://%s:%s', host, port);
});

这会输出有用的 SVG html,但我还想要第二个响应字段 encoded,它应该包含以下内容,我可以在我的 HTML 电子邮件中使用:

"encoded": "data:image/png;base64,iVBORw0KGgoAAAANSUhEU...etc"

如何编码这个 SVG?如果可能的话,我想避免任何文件写入,并直接从 SVG 到编码图像。另外,我知道您可以在电子邮件中使用 SVG,但我更希望它采用编码图像格式。谢谢!

【问题讨论】:

    标签: javascript node.js d3.js svg


    【解决方案1】:

    为了将其作为 png 图像发送,您首先必须对 svg 进行光栅化,这是实际的问题。这已在多个地方得到解答,但this one 包含许多好的解决方案。您可能应该使用 imagemagick 进行转换。由于您想避免写入中间文件,因此该答案表明您可以将 svg 内容通过管道传输到转换器的标准输入。

    接收到的缓冲区可能是directly encoded to base64

    【讨论】:

    • 谢谢,如果我允许写入文件系统,情况会大不相同吗?
    • 在这种情况下,您不会将 svg 通过管道传输到转换器,而是将 svg 保存到文件中,然后将文件名传递给转换器。所以,差别不大。也请查看 imagemagick 文档以获取一些提示。
    【解决方案2】:

    将根&lt;svg&gt; 节点的"xmlns" 属性设置为"http://www.w3.org/2000/svg" document 内的&lt;svg&gt; 节点或.attr()

    data URI 方案包括

    data:[<media type>][;base64],<data>
    

    由于我们知道&lt;media type&gt; 将是image/svg+xml,并且我们想要结果数据的base64 表示,我们可以定义一个变量,我们将连接base64base64 表示。

    let data = "data:image/svg+xml;base64,";
    

    然后我们得到&lt;svg&gt;元素的.outerHTML

    // optionally preceded by `XML` declaration `<?xml version="1.0" standalone="yes"?>`
    let svgString = svg[0][0].outerHTML; 
    

    svgString为参数调用btoa()

    btoa(data) 方法必须抛出一个“InvalidCharacterErrorDOMException 如果 data 包含代码点为 大于 U+00FF。否则,用户代理必须将 data 转换为 一个八位字节序列,其第 n 个八位字节是八位表示 data 的第 n 个字符的代码点,然后必须 将 base64 算法应用于该八位字节序列,并返回 结果。 [RFC4648]

    let base64 = window.btoa(svgString);
    // concatenate `data` and `base64`
    let dataURI = data + base64;
    
    response.send({
                    "html": window.d3.select("body").html(),
                    "encoded": dataURI
                 });
    

    let svg = window.d3.select("svg")
      .attr("xmlns", "http://www.w3.org/2000/svg")
      .attr("width", 100)
      .attr("height", 100);
    
    svg.append("rect")
      .attr("x", 10)
      .attr("y", 10)
      .attr("width", 80)
      .attr("height", 80)
      .style("fill", "orange");
    
    let data = "data:image/svg+xml;base64,";
    let svgString = svg[0][0].outerHTML;
    let base64 = window.btoa(svgString);
    let dataURI = data + base64;
    
    console.log(dataURI);
    
    document.querySelector("iframe").src = dataURI;
    <script src="https://d3js.org/d3.v3.min.js"></script>
    <svg></svg>
    <iframe></iframe>

    【讨论】:

    • 非常感谢,但不幸的是,我认为我不能在 HTML 电子邮件中使用脚本标签。另外,使用 iframe 而不是 img 有什么好处?
    • @Ed0906 &lt;iframe&gt; 包含在 stacksn-ps 中只是为了证明生成的 data URI 代表 &lt;svg&gt; 图像
    【解决方案3】:

    使用 NodeJS 构建 d3 图表并将其转换为编码的 PNG 而不使用文件存储的完整解决方案(大多数云托管所必需的)

    let express = require('express');
    let app = express();
    
    let jsdom = require('jsdom');
    let Buffer = require('buffer').Buffer;
    let svg2png = require('svg2png');
    
    app.post('/chart', function (request, response) {
        try {
            jsdom.env(
                "<svg id=\"svg\"></svg>",
                ['http://d3js.org/d3.v3.min.js'],
                function (err, window) {
                    let svg = window.d3.select("svg")
                        .attr("width", 100)
                        .attr("height", 100)
                        .attr("xmlns", "http://www.w3.org/2000/svg");
    
                    svg.append("rect")
                        .attr("x", 10)
                        .attr("y", 10)
                        .attr("width", 80)
                        .attr("height", 80)
                        .style("fill", "orange");
    
                    let data = "data:image/png;base64,";
                    let svgString = svg[0][0].outerHTML;
                    let buffer = new Buffer("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + svgString);
                    let promise = svg2png(buffer, {width: 300, height: 400}); // This options block is optional but height and width should be specified on the SVG HTML otherwise
                    promise.then(buffer => {
                        let dataURI = data + buffer.toString('base64');
                        response.set('Content-Type', 'application/json');
                        response.send({
                            "html": svgString,
                            "encoded": dataURI
                        });
                    });
                }
            );
        } catch (err) {
            console.warn(err);
        }
    });
    
    // Run Server
    let port = process.env.PORT || 8305;
    let server = app.listen(port, function () {
        let host = server.address().address || 'localhost';
        console.log('Example app listening at http://%s:%s', host, port);
    });
    

    响应中的 encoded 字段可以在 HTML 电子邮件中使用:

    <img src="{encoded-value}"/>
    

    例如

    <img src="data:image/png;base64,iVBORw0KGgoAAA..."/>
    

    【讨论】:

    • 它可以工作,但你必须使用 jsdom 9.12 或更早版本,因为现在不推荐使用 jsdom.env (2020)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 2015-07-19
    • 1970-01-01
    相关资源
    最近更新 更多