BT

您是否属于早期采用者或者创新人士?InfoQ正在努力为您设计更多新功能。了解更多

SQL Server报表服务以及使用重叠数据

| 作者 Grzegorz Gogolowicz 关注 0 他的粉丝 , Trent Swanson 关注 0 他的粉丝 ,译者 李高任 关注 0 他的粉丝 发布于 2010年1月6日. 估计阅读时间: 30 分钟 | ArchSummit社交架构图谱:Facebook、Snapchat、Tumblr等背后的核心技术

代码可从MSDN代码库下载

导引

作为业务的一部分,许多公司都需要由扫描图像或各种官方支持的不同格式来生成报表和表格。对于这些文档,通常都有着严格的格式要求,甚至连对文本框、标签大小和字体样式的丝毫变动都不允许。而且,重新创建与原始表格完全一致的报表,是一项代价可能很大、也非常容易出错的任务。填充表格的数据通常存储在关系型数据库中,在输出的表格中,必须将它们显示在准确的位置上。这种情况有时被称为固定布局的报表,有时被叫做像素级完美报表。

本文基于SQL Server 2008的报表服务(SSRS)讨论了针对这一问题的解决方案。在本方案中,我们假设有一个表格以由政府或其他有关当局指定的PDF或图像格式的方式输入。该表格的构成是:固定元素(各种标题、标签和说明)和可变文本或图像元素,这些变化的部分依赖于存储在企业数据库中的数据。另外,这个表格可能会是单页或多页的。

场景

一家企业扫描或下载了一张官方表格,以生成员工信息表。它可能是图片,也可能是PDF格式,其实任何以PDF格式提供的表格都可以很容易地转换为图像。在此例中,我们使用“关于工伤/病的雇主报告 ─ 纽约州 ─ 劳工赔偿局”。

由于PDF格式的文件很容易转换为图像格式,我们将转换出来的图像作为新SSRS报告的背景。该图片中有源表格中的所有固定元素项。在SSRS的报表设计器中,我们在所有需要显示数据库中信息的位置上添加文本框和复选标签,并根据其背景图像上的线条来设定它们的位置。因此,该图片将被数据库中的数据以准确的位置加以覆盖。该报告可在所有Web浏览器的页面中显示正确且不失真,并能够在不损失质量的前提下进行打印。用户还可以输出报告到TIFF,PDF格式,或者SQL Server Reporting Services支持的其他格式。

解决方案概述

最核心的需求是在所有典型场景下对报表应用支持的灵活性。这意味着,报表要能在所有浏览器的上无失真的正确显示,以及能够无质量损失的进行打印输出,当然,还应该有一个导出报表不同格式的选项,如TIFF或PDF格式。

挑战

本解决方案中出现了一些不同的挑战,主要是由于报告要按照我们的要求在网络浏览器显示和打印。浏览器通常不能理解或支持图像编码中”点每英寸”(DPI)的计量单位,而总是以96 dpi分辨率来显示图像。 96 dpi的精度在打印报告和表格时通常也不能被接受,而是更高的分辨率,一般300 dpi或以上,才能够达到可接受的印刷质量。

A:HTML渲染时使用的96 dpi分辨率光栅被ReportViewer组件用于浏览屏幕上的图像。与此同时,在有打印要求的其它场景下,需要较高的分辨率(300 dpi或更高)才能保证可接受的文档打印质量。

B:当使用高清晰度的图像时,打印预览将消耗大量资源,在某些情况下甚至会因为性能太低和内存溢出而无法使用。

C:HTML渲染不支持元素的叠加显示。这意味着,如果在报表中包含有放置在图片图像元素(如背景或表单中的图像)之上的文本框(表单中的某个字段域),那么它在浏览器中将不会被显示在图片在上面,而是在最右边。而其他一些内置的渲染器是支持元素叠加显示的。

权衡和决定

各种业务都需要固定布局的报表,它们往往会有许多不同的需求,而这些需求都有着不同的优先级。我们将各种不同的需求和多样的选择列出来,一起讨论不同方案的利弊。我们将不会触及这些方案的细节,而是讨论如何设计和实现一种能满足既要让报表在浏览器中显示、又可以进行高分辨率打印需求的方案。

所有场景都使用一个96 dpi 分辨率的背景图片

利:这种方法意味着解决问题的最简单方法。

弊:以96 dpi 分辨率的图片为背景的报表在ReportViewer里应该能正确显示,但在打印或输出为PDF时,质量应该会很差。

所有场景都使用一个高分辨率的背景图片

利:报表基于高分辨率图像组件,可以获得高品质印刷效果。

弊:报表在浏览器中的效果不会像打印它时一样。预览进程可能会因消耗大量资源而表现不佳,并很可能最终导致内存溢出的异常。

在一个报表中混合使用低分辨率和高分辨率的图像

利:将一个报表内同时放入低分辨率和高分辨率图像,能够涵盖最典型的应用场景。

弊:这种方案在设计每张报表和在进行可复用的开发时,都需要额外的代价。这种方案的另一种实现方法是针对网页和/或Windows应用实现一个定制的报表查看器。

高低双分辨率解决方法

此解决方案在一份报表中使用两种不同分辨率的图像,一张96 dpi的图片和一张本例中为300dpi的高分辨率图片。ReportViewer默认就定制有此功能,可以在不同场景中选择正确处理的图像,如在浏览时和打印时。

在低分辨率时对打印质量低下的挑战(见上文挑战A),通过在每页中使用两个不同版本的图像,并执行一个特殊的逻辑在浏览和打印时来切换这些图像得到了解决。

第二个挑战是在高分辨率图片时如何在屏幕上正确显示报表(见上文挑战B),这个只要不在浏览和打印预览时使用高分辨率图像就解决掉了。我们在WinForms报表查看器中指定预览时使用低分辨率的图像,在网页激活打印时使用PDF格式预览。这使我们能够显著减少对性能的影响和内存的压力。

第三个挑战是HTML渲染不支持元素重叠显示(见上文挑战C),只要不使用HTML渲染器,而使用内建的JPEG渲染器就可以解决这个问题,一次只将报表的一个单页渲染成一张JPEG图像,并建立一个自定义报表查看器来浏览报表中的每个单独页面。

至此,我们已经创建了两个自定义ReportViewers:一个是针对Windows应用,基于WinForms的 ReportViewer控件;另一个是针对Web场景,基于自定义aspx页通过web请求充分利用SSRS的功能,同时内部调用WebForm ReportViewer控件。报表每一次在屏幕上显示时都会使用96 dpi的低像素图片,而在打印或导出报表时就会使用高分辨率的图片。由于报表的代码本身不能确定是在什么环境中被使用,我们就在报表中增加了一个名为ForPrint的布尔型参数。我们的自定义代码会在报表是在被显示在屏幕上时将该参数设置为False,而当报表是在被打印或输出时,将其设置为True。

我们的解决方案见下图所示:图1所示的是针对Windows应用,图2是针对各种Web应用场景。

图1 Windows应用程序解决方案概览

图2 Web应用场景解决方案概览

实现说明

如前所述,我们解决的挑战之一(上文提到的挑战C)是与网络浏览器的图像失真有关:默认SSRS的HTML渲染器会让文本框和复选框控件、与设计好的相对于背景图片的位置有轻微偏移。产生这一结果的原因是,HTML标签生成的报表包含有一个HTML表格/网格和叠加于网格不同单元格中的控件(如编辑框)。这些单元格的坐标在不同的浏览器会以不同的方式计算(例如,Internet Explorer 7似乎是四舍五入到下一个整数),而且偏差会随着行和列的增加替增。不同的浏览器都会让最终的图像显示有不同的失真,但却没有一个浏览器能让报表实现像素级完美的呈现。

一种可能的解决办法是开发自定义的HTML渲染器(查看MSDN的文章可以获得更多关于自定义渲染器的资料)来渲染那些经常用于固定布局的报表组件,例如文本框,图像,Tablix等。使用自定义渲染器已经超出了我们的示例范围,我们决定使用更简单的解决方案:用一个自定义的aspx页面代替默认的ReportViewer.aspx来生成固定布局的报表。浏览报表时,这个自定义页面通过定制相应的网址发起Web请求来调用SSRS得到报表的各页,它们是JPEG格式的96 dpi图像。在报表各页之间浏览并打印或者导出时,我们在页面顶部添加自定义按钮。打印的实际上是后台导出到PDF,因此实际上是后台生成的PDF文档被发送到打印机,而非网页显示的内容。

我们的方案关注于解决基本问题:通过在表格图像上叠加数据来生成报表,我们已经说明了其可行性。一些个别的报表可能需要额外的自定义代码。例如某些表单的字段,跨越了几行,每行的长度也可能不尽相同,这使得标准的报表组件很难处理。类似的还有,一些表单有些固定字段,如SSN就是由多个独立的单元格组成。对于这些额外要求,可以通过定制报表组件的报表设计时行为和运行时行为来实现:该设计时行为允许精确定义每条件的尺寸和字符串“切分之后”填充到单元格的每个字符和准确位置;而运行时行为则会考虑在设计时就已经定义好的尺寸和“间距”。更多关于自定义报表项的信息你也可以在这里找到,但是我们认为实现它们已经超出了本文的范围。

实施细节

我们的解决方案包括三个部分,每个都由一个Visual Studio 2008的项目代表:

  1. 图像上重叠数据的多页固定格式报表示例
  2. 在Windows程序中浏览,打印,导出各种报表的通用解决方案示例
  3. 基于Web的浏览,打印,导出各种报表的通用解决方案示例

环境要求

要打开并运行它需要安装Visual Studio 2008,SQL Server 2008并将这两个产品正确地集成,最简单的集成方法就是将两种产品都安装在本地。

对于PDF格式页面的浏览,并从网上打印网页,您将需要安装Adobe Reader 6或以上版本。

为有图表叠加内容的C - 2表格创建一个SSRS的报表

以下是如何使用SQL Server 2008 Reporting Services创建一个多页固定布局的图表叠加报表的步骤说明。在此例中,我们使用一个命名为“关于工伤/病的雇主报告 ─ 纽约州 ─ 劳工赔偿局”的多页表格 -以下文简称C2表格。创建一个单页报表可以按照如下步骤进行。

示例报表是完整可用的,可以通过在Visual Studio 2008中打开“Fixed Layout Image Overlay Report.sln”工程来查看。报表需要使用的数据结构和样例数据,可以在SQL Server Management Studio中执行“SQLScripts”目录下的“C2SampleData.sql”脚本来生成。

第1步 ─ 创建表单图像

每个表单页面都创建两个图像:即每页都有一个96 dpi的版本(下文称之为 c2_Page__96,用于屏幕浏览)和一个300 dpi的版本(c2_Page_,用于打印)。

我们还创造了一个用于检查的小图像(“Check.jpg”)。

第2步 ─ 创建项目

打开Visual Studio 2008并创建一个报表服务器项目。添加一个新的报告,不要使用向导(右键单击解决方案资源管理器,选择“Add” ->“New Item”。在“Add New Item - Report Project 1”窗口中选择“Report”,然后点击“Add”按钮)。

第3步 ─ 将图像添加到项目

将所有在步骤1中创建的图像添加到报表项目中。在此演示项目中,我们将使用最简单的方法在项目嵌入图像,但需要注意的是,当图像文件总的大小超过报表服务器对rdl文件尺寸的限制时,它将无法工作。这种情况下,应该使用外部图像。

第4步 ─ 添加模式参数

添加一个隐藏的名为ForPrint报表参数名,布尔类型,默认值为“false”。这将用于以编程方式切换低、高分辨率的背景图像。要添加该参数,在“Report Data”窗口中的右键点击“Parameters”选择“Add Parameter”即可。

第5步 ─ 添加报表主Tablix

从工具箱中将“List”报表组件添加到“Body”区域。在“Data Source Properties”窗口中设定数据源信息(它会在拖进"List"控件之后自动弹出),然后在Data Source栏中编写查询来提供数据。重命名Tablix为BaseTable,从工具箱把拖动矩形控件到BaseTable区域并重命名为BaseRectangle。

第6步 ─ 设置页面大小和分页符

在“Report Properties”对话框中选择纸张尺寸(8.5in * 11in),并设置边界距都为0的。

调整区域大小,使之为一个查询记录的所有页面的大小之和(例如,8.5in * 11in是一张信纸的大小,8.5in * 22in是两张信纸的大小,8.5in *(11in * N)是N张信纸的大小)

将BaseTable的位置设置为 (0in; 0in),并将其尺寸调整到与Body大小相同。设置BaseRectangle的KeepTogether属性置为False,以允许软分页方式。

第7步 ─ 添加页面

为每个报表页面的BaseRectangle容器添加一个矩形(检查该矩形的Parent属性,其值必须是“BaseRectangle”,而不是“Body”!),将其命名为Page_

矩形的大小是基于原报告页面的大小,位置为x = 0,y<=(之前所有页面高度之总和),例如: (0in; 0in),(0in; 11in),(0in; 22in)。

第8步 ─ 添加低分辨率的背景图像

低分辨率的图像将只用于报表在屏幕的显示——因为如果用它来打印,质量会很差。

我们将使用的页面矩形的BackgroundImage的属性,因为所有的渲染器都支持它。按照下面的说明更改所有Page_矩形的属性:

  • Source = "Embedded";
  • Value = "=IIF(Parameters!ForPrint.Value = false, "c2_Page__96", "")",n是指定矩形所在的实际页码
  • BackgroundRepeat = "Clip"

这样,我们只有当页面是在屏幕上显示时使用低分辨率的背景图像和并在打印时隐藏它(根据ForPrint参数值进行判断)。

第9步 ─ 添加高分辨率的背景图像

对于打印操作,我们将使用高分辨率背景图像,并以Image报表控件作为每个页面的矩形的背景(矩形的BackgroundImage属性不支持图像拉伸,所以我们需要为每个报表页添加额外的Image控件)。HTML渲染器不支持这种高分辨率图像的背景,而且它将消耗大量系统资源,所以我们将只用在打印和PDF导出操作上。

对于每个报告页面添加一个Image控件到BaseRectangle,将其命名为:Page__Image)。将其大小定义为原始报表页的大小,位置为x = 0,y=<(之前所有页面高度之总和),例如: (0in; 0in),(0in; 11in),(0in; 22in),因此,它的尺寸和位置与在同页上的矩形大小完全一致。大小和位置数值的单位是英寸或者毫米,而不是点。

更改Page__Image的属性如下:

  • Source = "Embedded"
  • Value = "c2_Page_", < n> is appropriate page number
  • Sizing = "FitProportional"
  • Hidden = "= IIF(Parameters!ForPrint.Value = true, false, true)"
  • 在每一个图片上应用工具栏上的“Send To Back”按钮(右键单击图片->Layout->Send To Back,或打开Layout面板:View->Toolbars->Layout)

经过这样的设置,Image控件只在当ForPrint参数值为True时可见。

第10步 ─ 添加文本框

各种报表数据都可以很容易地通过Text报表组件来展现。从“Report Data”标签中将“字段”拖到选定的Page_rectanglle中(不要忘记检查它的Parent属性!)。你可以指定文本框的位置,大小和字体,以符合原始报告的外观。对每个数据字段重复此操作。

请务必将CanGrow属性设置为false(默认为true),并确保CanShrink属性值为false。

第11步 ─ 添加复选框项

为了达到原始PDF表格中的复选框的效果,基于该字段的数据逻辑(如:性别=“男”),我们将设置一个小的图片的visible属性(置为“checked”)使其可见。

要创建一个复选框添加一个Image控件,设置好它的位置,并调整其大小。更改其属性为:

  • Source = "Embedded"
  • Value = "= IIF( = true, "Check", ""),为这个域使用一些逻辑
  • Sizing = "FitProportional"

第12步 ─ 部署

到此,该报表已经可以用于在报表服务器上部署。设置“Project Deployment > properties”:

  • TargetDataSourceFolder = "Data Sources"
  • TargetReportFolder = "FixedLayoutReport"
  • TargetServerURL = http://localhost/ReportServer(或者你自己的报表服务器地址)

之后,就可以部署它:

在接下来的部分中,我们将展示如何在不同场景中使用已布署好的报表。

如果您还没有从零开始创建报表,而是希望直接使用示例ImageReportC2中的C - 2报表,那么,您需要在Visual Studio 2008中打开此报表,执行第12步将其部署到报表服务器。

在使用此报表运行下一个示例之前,请确保该报表的数据源正确的链接到了已经安装有示例数据库的SQL Server 2008实例上。确认这一点,只要查看DataSource.rds的连接属性,该数据库的名称应该是C2,而且,与SQL Server的连接认证测试必须是成功的。

请注意,当报表需要由许多数据行(超过10,000行以上)生成时,报表服务器上的资源消耗将大幅增长。推荐使用64位系统制作这类报告。

创建一个可以在Windows应用程序中浏览、打印和导出固定布局报表的通用解决方案

下一步,是创建一个通用应用,允许Windows应用中程序中浏览、打印和导出固定布局的报表。在本例中,我们将使用WinForms ReportViewer控件和报表服务器远程处理,以及可自动处理ForPrint参数的自定义代码。

要实现这个应用,你可以创建一个新的WinForms项目,将工具箱中的ReportViewer控件到窗体上,按照下图设置ReportServerURL和ReportPath属性:

然后,你需要实现几个事件处理程序来正确管理ForPrint参数。在ReportExport和Print事件处理器中,该参数必须设置为true,见下例:

private void rvMain_Print(object sender, CancelEventArgs e)
{
    //Reset report to high-resolution mode for printing
    rvMain.ServerReport.SetParameters(new ReportParameter[]
	{ new ReportParameter("ForPrint", "True") });
}

在RenderingBegin和FormActived事件处理器中,该参数需要被置回到False,见下例:

private void rvMain_RenderingBegin(object sender, CancelEventArgs e)
{
    //Reset report to low-resolution mode for screen rendering
    if (rvMain.ServerReport.GetParameters()[0].Values[0] == "True")
    {
	rvMain.ServerReport.SetParameters(new ReportParameter[]
{ new ReportParameter("ForPrint", "False") });
    }
}

要查看示例应用程序,在Visual Studio 2008中 打开到WinFormsViewer目录下的FixedLayoutReportWinFormsViewer.sln解决方案。由于该解决方案引用了示例C - 2报告,它(或其它被引用的报表)必须是已经被部署到服务器上的(上文第12步说明了如何进行部署)。

在运行示例应用程序之前,在app.config文件中指定报表的实际位置。你可以编辑ReportWinForms.Properties.Settings成员框内的两个设置:

  • “ReportServerURL”─这里填写部署了C-2报表的报表服务器地址
  • “ReportPath”─报表名称。本例中,它的值是/FixedLayoutReports/c2

如果你不能确定报表服务器的URL,可以在报表服务配置管理器中的“Web Services URL”页中找到它。

图xx显示了一个由示例应用基于C2表格图片生成的图表数据叠加C-2表单

创建一个基于网页的用于浏览,打印和导出固定布局报表的通用解决方案

在实现说明环节中已经提到,基于Web解决方案的关键部分是使用一个自定义aspx页面来替代缺省的ReportViewer.aspx页面执行其角色,但它被专门设计用于解决默认的ReportViewer处理固定布局报表时所出现的问题。我们的自定义页面包括:

  • 一个单独的标准Image WebControl
  • 几个用来在报表页面间导航的控件(第一页,上一页,下一页和最后一页按钮,当前页和最后一页文本框)
  • 下拉列表控件,它包含了可用的导出格式:MHTML,PDF和TIFF。这里我们并没有考虑Word和Excel格式因为它们在固定格式导出应用中并不普遍。
  • 导出按钮,用来执行导出到指定格式。
  • 打印按钮,把报表页导出到PDF格式,然后调用Adobe PDF进行打印预览。

“Web Viewer”目录包含了我们基于Web的解决方案。要查看请在Visual Studio 2008中打开“FixedLayoutReportWebViewer.sln”。在运行示例程序之前,请打开Web.config文件,在configuration/appSettings配置区指定报表服务器的实际URL。

报表名和路径应该作为参数附加到URL串中。示例解决方案中报表名应该是“/FixedLayoutReports/C2”。要设置该项,打开Property Pages>Start options>Specific Page,把已对斜线等字符进行过编码的报表URL填写到报表路径中:

FixedLayoutReportViewer.aspx?%2f FixedLayoutReports%2fC2

或者,你可以在浏览器地址栏中指定该地址。要注意的,本解决方案依赖于报表服务器URL访问机制,尤其是当命令和格式都是在URL参数中指定时更要注意。例如,要在以JPEG格式显示一个指定的页面,我们使用下面的URL参数:

(rs:Command=Render&rs:Format=IMAGE&rc:OutputFormat=JPEG&rc:StartPage=).

如前所述,不使用HTML渲染器,而是通过将整个报表中的每一页都以一个单独的jpg图片的方式来呈现,这解决了失真问题(上文提到的挑战C)。导出功能是通过在报表URL中包含导出命令("rs:Command=Export")和格式参数(e.g. "rs:Format=MHTML")实现的。对于PDF输出高分辨率的图像我们通过设置报表URL的ForPrint参数值为True实现。要打印文件,我们执行交互式PDF导出,一段JavaScript脚本将调用PDF对象的PrintWithDialog()方法实现打印。

结论

读过本文,你知道了如何使用SQL Server 2008 Reporting Service来创建符合官方标准格式的精确报表。本方案通过使用符合正确用途的图片作为背景图像来创建固定格式的表格,最大限度地减少了所需的工作量。我们还演示了哪些报表可以在基于Windows应用程序和基于Web应用程序的解决方案中进行处理。

查看英文原文:SQL Server Reporting Services and Working with Overlay Data


感谢张海龙对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

非常好的思路,但在非IE浏览器下怎么办? by hang li

解决Web套打非常有用的思路,这样就可以由美工直接设计报表格式,不用再纠结ReportDesigner
只不过打印还是需要用到ActiveX控件RSClienprint.cab,非IE浏览器怎么办?

Re: 非常好的思路,但在非IE浏览器下怎么办? by withyou gakaki

实际上非微软阵营早有解决方法了那就是pdf,这样的解决方案不难找

Re: 非常好的思路,但在非IE浏览器下怎么办? by Zhang Robin

是说这种报表专门打印需要ActiveX吗?还是所有的?

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

3 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT