MaltReport2:通用文档生成引擎

UPDATED: 本文仅适用 MaltReport 2.x ,3.x 版本文档还在撰写当中,目前请参考项目中的 Samples。

MaltReport 是我几年前写的开源单据、报表引擎,最近进行了较大的更新,尤其是几年来在生产项目中应用取得了非常好的效果,特别写篇介绍文字给大家分享一下。

首先先介绍几个名词:

  • OpenDocument:国际标准文档格式,开源办公软件 OpenOffice.org/LibreOffice 的 ODT/ODS 即为 OpenDocument 格式。
  • OfficeOpenXML:同样是国际标准文档格式,由 Microsoft 定义,MS-Office 的 DOCX/XLSX 等即为 OfficeOpenXML 格式。

简介

MaltReport 实际上是一个通用的模板文档生成系统,其用途不仅用于生成报表,也可以用来生成合同、预算报告、标书等等任何需要格式与数据相结合的文档,其中的 XLS/ODS 模板尤其适合大量数据导出的场合。

简单来说,MaltReport 是通过直接在内存中解析操作 OpenDocument 和 OfficeOpenXML 文件来实现报表和单据的生成,整个理念非常接近于 ASP.NET MVC 的 Razor 模板,只不过 Razor 生成 HTML 而 MaltReport 生成 ODT/ODS/DOC/XLS 文件。

MaltReport 的优点:

所见即所得

  1. 通过在 OpenDocument 或 OpenOfficeXML 文件里嵌入简单易学的 Velocity 模板语言来开发模板,实际上我们将 MS-Office 和 LibreOffice 作为我们的所见即所得的报表模板编辑器。
  2. 生成后的报表是通用的 XLS/DOC/ODT/ODS 文件,打印、格式转换等均不是问题。
  3. 简单易用的 API,三行代码实现报表生成。

性能与可靠性

直接在内存中操作 odt/ods/xls/doc 文件,采用 NVelocity 模板引擎进行内容的替换,生成报表不依赖 Libreoffice/MS-Office 等软件,适合服务器端运行。因为 Word/Excel 这些桌面软件不是为服务器长期运行设计的,之前有些 Word 文档生成工具之类需要通过 Word 的 COM 接口操作 docx 文件,数量一大很有可能耗尽服务器的内存。

MaltReport 报表引擎本身不负责报表的排版和显示,因此没有其他采用像素定位设计的报表工具所存在的中文换行、对齐等等布局问题,极大提高了报表生成的性能。经实际使用的经验显示,生成报表的速度仅受限于磁盘 IO 速度.

特性完整

  1. 可以利用 LibreOffice Calc 或 MS-Excel 电子表格的强大功能,进行二次汇总分析或绘制图表。
  2. 支持图像数据,可以在文档中嵌入用户提供的图像数据。

还有最后不能不提及的,免费开源,MIT 协议授权,可在商业产品中自由使用。

使用说明

下面以 Word 报表为例介绍 MaltReport 的使用。

第一步,在 nuget 中加入项目引用:

MaltReport 已发布到 nuget.org 中,可通过 nuget 引入您的项目,参考:

https://www.nuget.org/packages/MaltReport2

第二步,创建报表模板

新建一个 Word 文档,并另存为【Word 2003 XML】格式,命名为“template1.xml”。注意这里 Word 会把文件的扩展名设为 XML。

在 Word 中创建模板,报表引擎通过特殊的超链接及 Velocity 模板标记来识别,一点简单的小介绍:

  1. $xxx 是模板的占位符,通过 RenderContext 提供的数据进行替换,也支持 $xxx.yyy.zzz 或 ${xxx.yyy.zzz}这样的格式。
  2. 若表达式太长可使用超链接,超链接使用 rtl://$xxx.yyy.zzz/ 或rtl://${xxx.yyy.zzz} 的格式。
  3. 支持 foreach 循环和 if-then-else 条件等,尤其是表格可以按行或按列循环。

实际例子可参考 Velocity 的 VTL 语言文档及本项目的演示。

 当模板创建完成以后保存并关闭 Word。

Excel 的模板也是类似的操作:

注意,显示为 #VALUE! 的单元格是因为我们把此单元格设为数字格式,但是模板占位符不是数字所以 Excel 报错,但并不影响报表生成。

第三步,在 C# 代码里加载、渲染并生成报表

 1 var dt = new DataTable("Employees");
 2 
 3             //Fill the DataTable
 4             var connectionString = @"Version=3,uri=file://./Database/northwind.db";
 5             using (var connection = new SqliteConnection(connectionString))
 6             {
 7                 var sql = "SELECT FirstName, LastName, HireDate, BirthDate, Address FROM Employees";
 8                 var adapter = new SqliteDataAdapter();
 9                 adapter.SelectCommand = new SqliteCommand(sql, connection);
10                 adapter.FillSchema(dt, SchemaType.Source);
11                 adapter.Fill(dt);
12             }
13 
14             var renderContext = new Dictionary<string, object>()
15             {
16                 //Plain old types
17                 {"title", "EMPLOYEES"},
18                 {"property1", "Property 1"},
19                 {"property2", "Property 2"},
20 
21                 //Strong types
22                 {"orm_employees",
23                     new List<Employee>()
24                     {
25                         new Employee("Micheal Scott", "Address 1", 22),
26                         new Employee("Andy Bernard", "Address 3", 33),
27                         new Employee("Dwight Shurte", "Address 1", 22),
28                         new Employee("Jim Halpert", "Address 2", 27),
29                         new Employee("Pam Beesly", "Address 4", 19),
30                     }
31                 },
32 
33                 {"employees", dt}, //DataTable is ok
34 
35                 {"now", DateTime.Now}, //DateTime is ok too
36             };

上面的代码演示了 RenderContext 的概念用法,RenderContext 为模板中所包含的数据的容器,本身是一个 IDictionary<string, object> 类型,key 为变量名,value 为变量值,变量值支持原始类型、强类型类、结构、DataTable 等。

有了模板和要填充到模板中的数据我们只需加载模板、编译模板然后渲染模板即可,非常简单直观的 API:

 1 var template = new WordMLTemplate();
 2 
 3 template.Load("template1.xml"); //第一步加载模板文件
 4 
 5 template.Compile(); //第二步编译模板
 6 
 7 //第三部渲染模板
 8 var resultDoc = template.Render(ctx); 
 9 
10 //第四步,保存生成的报表文件,也可保存到 MemoryStream
11 using (var resultFile3 = File.Open("result.doc", FileMode.Create, FileAccess.ReadWrite))
12 {
13     resultDoc.Save(resultFile3);
14 }

生成了名为“result.doc”的报表文件,试着用 Word 打开:

Voila! 全部搞定!

在项目的源代码里包含 Sandwych.Reporting.Demo 演示程序,里面包含生成 DOC/XLS/ODT/ODS 的全部样例。

下一步的开发计划

  1. 支持最新版 MS-Office 的 DOCX/XLSX 文档格式,因为我比较喜欢用 LibreOffice 做报表及打印工具,所以 MS-Office 的格式支持度没有 ODS/ODT 高;
  2. 支持二维码一维码图片生成及文档嵌入;
  3. 异步 IO 支持;
  4. 移植到 .Net Core,不过应该是个长期的过程,最少得等 .Net Core 2.0 出来以后。

常见问题解答

Q: 这到底是特么的什么东西?

A: 一句话来说 MaltReport 是 DOC/XLS/ODT/ODS 文档生成器。

 

Q: 这跟 NPOI 有什么区别?

A: MaltReport 只能生成不能读取 MS-Office 文件,但是单论生成的话 MaltReport 的性能远远超过 NPOI,而且不止一个数量级。MaltReport 跟 NPOI 的理念不同,不需要你用代码去设置 XLS/DOC 文件的样式、表格高度之类的格式工作,你直接在 Excel/Word 里设置好让 MaltReport 照着模板生成就可以了,程序需要提供的只是填充模板的数据。

 

Q: ODT/ODS 是什么文件,我怎么没见过?

A: ODT/ODS 是 LibreOffice/OpenOffice 使用的文档格式,ODT 等同于 DOCX、ODS 等同于 XLSX。LibreOffice 类似于免费开源版的 MS-Office。我们主力支持 ODS/ODT 文件是因为我们推荐使用 LibreOffice 作为报表设计、查看、打印及格式转换工具。举个例子来说,你可以把 LibreOffice 的“绿色版”打包到你的程序里,直接用作报表工具,这样难道不是很科学。还有更秒的是 LibreOffice 支持无界面后台网络服务的“无头”模式,你可以通过 RPC 直接访问 LibreOffice 的文件转换、打印各种功能。

关于调用 LibreOffice 实现文件格式转换请参考代码里的 Sandwych.Reporting.JODConverterDemo 项目。

项目地址及其他

联系我: oldrev AT gmail.com 也可加我 QQ: 55-43-1671

项目 github: https://github.com/oldrev/maltreport

项目 nuget:https://www.nuget.org/packages/MaltReport2

附加福利:支持图片、支持 .NET Standard 1.6 的开发版本在 vnext 分支里。

 

很惭愧,就做了一点微小的工作。如果觉得本项目对您有用,您可以在 github 上给我点星星/fork,或者点击本文下方的“推荐”,您的赞赏是我不断完善本项目的动力。

posted on 2017-02-06 11:55  李屠户  阅读(3140)  评论(7编辑  收藏  举报