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