Rails 如何在邮件中显示 HighCharts 渲染的图片

baypm2014 · 2015年02月11日 · 最后由 rubychinabz 回复于 2016年10月24日 · 5864 次阅读

【问题背景】 测试人员提出需要本人开发的平台支持质量报告的 web 页面以邮件形式发送的功能。但存在一个问题:在 web 页面中的所有饼图和柱状图等都是用 HighCharts 来实现的,但在邮件环境中是不支持 HighCharts 图片的显示。那么,这个问题该如何破解?

【解决思路】 首先想到的是能找到一个 HighCharts 的接口,可以将 highcharts 对象直接转为图片后,直接在邮件视图中引进即可。但受挫,HighCharts 没有提供类似接口。于是掉头考虑将 highcharts 绘制的 svg 图片转为 png 图片,终于在无尽的黑夜之中发现了点点繁星,发现可以在浏览器端将 svg 和 canvas 结合起来转成以 base64 编码的 png 文件,再将该文件转为 (解码)blob 对象 (本质是二进制数据流); 最后将该 blob 对象通过 ajax 技术回传给服务器,在服务器侧将 blob 对象转为 png 图片即可。发送邮件时只需将该图片以附件形式发送即可 (当然你也可以将图片保存在自己的文件系统中,邮件中直接引入超链接就可以)。

这里有一个核心问题:如何将 svg 转成 png? 解决方案:经过调研发现了一个叫 canvg 的库有如此功能,如获至宝! 该 js 库文档有一句:Allows for SVG -> Canvas -> png transition all on the client side(through toDataUrl) 它让 svg 转 png 的需求得以实现。 用法:

//在application中需要引入以下三个js库:
<script type="text/javascript" src="http://gabelerner.github.io/canvg/rgbcolor.js"></script> 
<script type="text/javascript" src="http://gabelerner.github.io/canvg/StackBlur.js"></script> 
<script type="text/javascript" src="http://gabelerner.github.io/canvg/canvg.js"></script>

//在实现转换的js中调用canvg函数将svg渲染到canvas上:
 window.canvg(canvas, svg); //render the svg on the canvas

实现原理

实现浏览器侧获取 blob 对象的代码:

//get the highcharts id
      $(".chart_object_div").each(function(){
          chart = $(this).html();
      });

       chart_object = $(chart).highcharts(); //get highcharts object
       svg =          chart_object.getSVG();  //get svg string
       canvas =        document.createElement('canvas'); //create a empty canvas
       window.canvg(canvas, svg); //render the svg on the canvas
       dataURL = canvas.toDataURL('image/png'); //convert canvas  to  base64 data

      //dataURLtoBlob is for Convert dataURL to Blob object
       var image_blob= dataURLtoBlob(dataURL);

      // Create new form data
       var fd = new FormData();

       // Append our Canvas image file to the form data
       fd.append(chart_id, image_blob)

       //send blob object to /versions/save_image 
       $.ajax({
         url: "/versions/save_image",
         type: "POST",
         data: fd,
         processData: false,
         contentType: false,
       });

实现服务器侧邮件中图片显示的代码:

// convert blob object to png file in Controller
png_file = "#{Rails.root}/public/email_images/#{params[key]}" + ".png"
File.open(png_file, 'wb') do |f|
     f.write(params[key].read)
end

//attach the png file in Mailer
attachments.inline[filename] = File.read(file)

//add  image link in mail view
<% inline_image = filename%>
<%= image_tag( attachments.inline[inline_image].url ) %>

【Tips】 rails 项目中邮件功能的 3 个要点:

  1. 不要企图复用针对网页浏览的视图,不同的邮件客户端对 html 的支持是不同的,否则会导致样式显示错误;
  2. 不要企图在邮件视图中调用含 cookies 或 session 的 helper, Mailer 环境是没有 cookies 和 session 的,建议所有值都从外边的 controller 传到 mailer 中;
  3. 如果需要使用 ActiveMailer 功能,强烈建议先将 Rails 升级到 4.1 以上。否则,呵.....呵....,闭上眼睛调试代码会是什么感觉?只能"享受"没有错误 log 靠直觉来定位问题所在了。

【参考文档】 http://www.highcharts.com/docs/export-module/export-module-overview https://github.com/gabelerner/canvg http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/highcharts/exporting/offline-download/ http://www.hcharts.cn/api/index.php#Chart.getSVG http://rohitrox.github.io/2013/07/19/canvas-images-and-rails/

ps. 从 c++ 转 rails 开发,接触 rails 刚半年的同时也是刚接触前端开发半年的时间,后台转前端的过程很痛苦,html, js,rails 等都是从 0 开始学 (改天会写篇博文记录一下这段艰苦的半年时光)。很多不懂的直接到 ruby-china 上发帖求答,在此感谢各位的帮助,祝新年快乐!

pps. 感谢一位名叫 xiaods(tommy) 的兄弟的指点,我才找到了这个问题的解决思路,再次感谢!

楼主的解决问题能力非常强。

对于邮件的 HTML, 只是内联样式,还有许多样式并不支持。

Rails4.1 很给力,写 mailer 已经很方便了。

赞~ 学习了!

#1 楼 @lyfi2003 谢谢,解决问题都是被逼出来的。还得继续努力。

值得学习感谢分享

versions/save_image 后台代码是什么?能不能发个 demo 给我?邮箱 [email protected]

为什么我的到这一步: fd.append('#container', image_blob) console.log(fd); 我想查看下传递的数据,里面是空的, image_blob 这个是有值的:Blob {size: 30124, type: "image/png"}

需要 登录 后方可回复, 如果你还没有账号请 注册新账号