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

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

【问题背景】 测试人员提出需要本人开发的平台支持质量报告的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)的兄弟的指点,我才找到了这个问题的解决思路,再次感谢!

共收到 7 条回复

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

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

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

赞~ 学习了!

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

值得学习感谢分享

versions/save_image 后台代码是什么?能不能发个demo给我?邮箱497412713@qq.com

#5楼 @q497412713 已发,请查收。

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

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