用FASTREPORT实现WEB应用中自定义报表

地球瘾

地球瘾

2016-02-19 20:49

在这个颜值当道,屌丝闪边的时代,拼不过颜值拼内涵,只有知识丰富才能提升一个人的内在气质和修养,所谓人丑就要多学习,今天图老师给大家分享用FASTREPORT实现WEB应用中自定义报表,希望可以对大家能有小小的帮助。

  开发WEB应用系统通常都会遇到报表打印问题。简单应用可利用IE的页面打印功能,利用HTML标签控制格式来实现。但复杂的业务型应用系统,报表不仅是组成应用的重要部分,还常常是相当复杂的。现在很多应用系统都要求提供自定义报表的功能——即客户可以自行设计、修改报表。

   

  C/S结构系统中,报表问题有很多成熟的解决方法。如DELPHI开发工具不仅自带有报表控件,还可以利用第三方控件来实现快速灵活的报表制作和打印,其中有名的控件是FR-Software & A.Tzyganenko 的FastReport。FastReport提供了能与DELPHI无缝集成的从设计到打印的完整控件包,提供的设计界面友好灵活,对于开发可让用户自定义报表的C/S应用来说,是一种很好的解决方式。

   

  B/S结构应用中,Crystal Report是一种大型报表系统常用和推荐的解决方案。但Crystal Report目前价格昂贵,而且该系统相当庞大。它的可定制性及精确控制打印效果方面尚不够完善。当然,在目前市场上,它仍是一种首选的WEB应用的报表解决方案。

   

  如果能将C/S应用中成熟的报表解决方案搬到B/S应用中,相信对于大部分开发人员来说,都是非常欢迎的。本文将讲述一个在JAVA环境中利用FastReport实现B/S应用中用户可自定义的报表解决方案。因为笔者近段时间正用DELPHI、JAVA做一些项目,所以样例代码就以DELPHI、JAVA编写。

   

  本解决方案样例的基本环境是:WINDOWS 2000 SERVER+SQL SERVER 2000+TOMCAT 4.0。开发工具:IntelliJ IDEA 3.0,DELPHI 5.0。客户端为IE 5.0浏览器。

   

  方案共要求用DELPHI编写两个程序,一个是将被包含在网页中并在浏览器中运行的ACTIVEX(.ocx),一个是运行在服务器端的报表处理程序,中间通过JAVA程序连接——或任何其他WEB语言都可以,如ASP、PHP等。方案的基本原理图如下:

   

  报表设计过程

   

  

报表打印过程

   

  

REPORT SERVER:可以做成一个普通的WINDOWS程序,也可以做成一个COM程序(Automation Object)。在报表设计过程中,用户端(ACTIVEX)向WEB SERVER发送报表设计请求,请求中包含要设计报表的名称;WEB SERVER 收到该请求后,调用REPORT SERVER请求设计的报表文件;REPORT SERVER收到请求后,先装载报表的数据环境,然后将报表设计文件(.frf)和该报表的数据环境文件压缩成一个包文件(.zip),将该包文件的完整路径名返回给WEB SERVER调用程序;WEB SERVER将包文件回送给用户端(ACTIVEX),用户端将接收到的包文件保存到本地硬盘上,并解压缩,从数据环境文件中恢复数据环境,通过FASTREPORT的相应控件打开报表文件给用户提供可视化设计。用户在ACTIVEX中设计报表时,虽然不能和数据库连接,但因数据环境已存在,所以用户仿如在通常的C/S应用结构下设计报表,能正常地看到报表的数据字典信息。在报表打印过程中,用户端(ACTIVEX)向WEB SERVER发送报表打印/预览请求,请求中包含要打印/预览的报表名称;WEB SERVER 收到该请求后,调用REPORT SERVER请求打印或预览的报表文件;REPORT SERVER收到请求后,先装载报表的数据环境,然后装载报表文件(.frf),接着在无界面状态下运行报表,最后将生成的已准备的报表文件(.frp)压缩成一个包文件(.zip),将该包文件的完整路径名返回给WEB SERVER调用程序;WEB SERVER将包文件回送给用户端(ACTIVEX),用户端将接收到的包文件保存到本地硬盘上,并解压缩,通过FASTREPORT的相应控件打开报表文件(.frp)给用户预览或打印或重新调整格式或输出为其他格式文件。用户在ACTIVEX中预览报表,仿如在通常的C/S应用结构下预览报表。

   

  WEB SERVER:提供通常的WEB服务功能。

   

  ACTIVEX:ActiveX是Microsoft提出的一组使用COM(Component Object Model,部件对象模型)使得软件部件可在网络环境中进行交互的技术集。它与具体的编程语言无关。作为针对Internet应用开发的技术,ActiveX被广泛应用于WEB服务器以及客户端的各个方面。本方案中的ACTIVEX控件主要做两方面的事情,一是利用FASTREPORT控件进行报表处理,包括报表设计(.frf文件)和报表打印(.frp文件)。一是与WEB SERVER进行通信,请求和接收包文件。本文样例的ACTIVEX采用DELPHI 5.0编写。

   

  下面分述各部分的一例具体实现(因为仅为说明方案的实现,所以很多代码细节都进行了简省)。

   

  一、             REPORT SERVER

   

  REPORT SERVER既可以做成一个普通的WINDOWS程序,也可以做成一个COM程序(Automation Object)。本例中为简化见,采用普通的WINDOWS程序实现。

   

  DELPHI中NEW一个应用程序。在FORM中加入TfrReport、TfrDBDataSet、ADOConnection、TADOQuery等控件——为了使用FASTREPORT的控件,需要安装该控件包,可从站点http://www.fast-report.com 下载,国内很多软件站点都提供该控件包的下载。其中TfrDBDataSet、TADOQuery控件视应用需要可加入多个,另外为了压缩文件,还要加入一个压缩控件,本例使用VCLZip。在Form1中加入三个函数:preDesignReport(rpFileName:String),prePrintReport(rpFileName:String),zipReportFiles(rpFileName:String),分别用于准备报表设计文件、准备报表打印文件、压缩报表文件 。Form1.Create方法为:

   

  procedure TForm1.FormCreate(Sender: TObject);

   

  var

   

          rpFileName,mode:String;

   

  begin

   

          if paramCount1 then

   

          begin

   

                  mode:=paramStr(1);

   

                  rpFileName:=paramStr(2);

   

                  if mode='d' then   //设计报表

   

                          if preDesignReport(rpFileName) then

   

                                  zipReportFiles(rpFileName);

   

                  if mode='r' then   //打印报表

   

                          if prePrintReport(rpFileName) then

   

                                  zipReportFiles(rpFileName);

   

          end;

   

          Application.Terminate;

   

  end;

   

  程序根据调用参数判断是准备报表设计文件还是准备报表打印文件,接着调用相应的过程来实现。最后的Application.Terminate 是让程序执行功能后即退出——因为这是服务端程序,是不能与用户交互的。

   

  preDesignReport(rpFileName:String)方法:

   

  function TForm1.preDesignReport(rpFileName:String):boolean;

   

  var

   

  ……   //其他变量

   

          dtfFileName:String;

   

  begin

   

          ……

   

  dtfFileName:=StringReplace(rpFileName, ExtractFileExt(rpFileName),'.dtf',

   

  [rfReplaceAll, rfIgnoreCase]);

   

          try

   

  rpAdoquery.SQL.Add('…');

   

                  rpAdoquery.open;//打开报表的数据环境

   

                  rpAdoquery.FieldList.SaveToFile(dtfFileName);

   

                  result:=true;

   

          except

   

              on Exception do

   

                  result:=false;

   

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

          end;

   

  end;

   

  函数preDesignReport的作用是准备报表设计文件。报表中可以引用多个DataSet,本例假设报表只引用一个名为rpAdoquery的DataSet。rpFileName 为报表文件名(.frf),DtfFileName为保存数据环境的文件名(.dtf)。因为用户端不能连接数据库,所以将DataSet中的Fileds通过rpAdoquery.FieldList.SaveToFile(dtfFileName)保存到文件,和报表文件一起传送给用户端的ACTIVEX,ACTIVEX利用.dtf文件复现报表的数据环境。

   

  prePrintReport(rpFileName:String)方法:

   

  function TForm1.prePrintReport(rpFileName:String):boolean;

   

  var

   

  ……

   

          repFileName:String;

   

  begin

   

          ……

   

     repFileName:=StringReplace(rpFileName, ExtractFileExt(rpFileName),'.frp',

   

  [rfReplaceAll, rfIgnoreCase]);

   

          try

   

  rpAdoquery.SQL.Add('…');

   

                  rpAdoquery.open;//打开报表的数据环境

   

                  frReport1.ShowProgress:=False;

   

                  frReport1.Clear;

   

                  frReport1.LoadFromFile(rpFileName);

   

                  frDBDataSet1.DataSet :=rpAdoquery;

   

                  frReport1.Dataset :=frDBDataSet1;

   

                  frReport1.PrepareReport;

   

                  frReport1.SavePreparedReport(repFileName);

   

                  result:=true;

   

          except

   

              on Exception do

   

                  result:=false;

   

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

          end;

   

  end;

   

  函数prePrintReport的作用是准备打印的报表文件,即先在服务器端装载报表并运行,将运行好的报表保存为文件,用于传送到用户端进行预览或打印。RepFileName是已准备好的报表文件名(.frp)。同样假设报表只引用一个名为rpAdoquery的DataSet。frReport1.ShowProgress:=False 使报表运行过程中不显示进度窗口(服务器端不能显示与用户交互的界面);接下来frReport1.Clear;…装载报表文件及设置相关数据属性;frReport1.PrepareReport 是在不显示预览窗口的情况下运行报表;frReport1.SavePreparedReport(repFileName) 将运行好的报表保存到文件,该文件传送给用户端的ACTIVEX,ACTIVEX可以直接预览或显示该报表。

   

  zipReportFiles(rpFileName:String)方法:

   

  function TForm1.zipReportFiles(rpFileName:String):boolean;

   

  var

   

  ……

   

          zipFileName,fileName:String;

   

          zipCount:Integer;

   

  begin

   

          ……

   

    zipFileName:=StringReplace(rpFileName, ExtractFileExt(rpFileName),'.zip',

   

  [rfReplaceAll, rfIgnoreCase]);

   

          fileName:= ExtractFileName(rpFileName);

   

          fileName:= ChangeFileExt(fileName,'.*');

   

          try

   

                  VCLZip1.ZipName:= zipFileName;

   

                  VCLZip1.RootDir:= '.';

   

                  VCLZip1.FilesList.Add(fileName);

   

                  zipCount:= VCLZip1.Zip;

   

                  if zipCount = 0 then

   

                          result:=false

   

                  else

   

                          result:=true;

   

          except

   

              on Exception do

   

                  result:=false;

   

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

          end;

   

  end;

   

  函数zipReportFiles的作用是把要传送给用户端的报表文件压缩为一个.zip文件,简化文件传送过程,而且压缩了数据量。ACTIVEX接收到.zip文件后,先解压出包中文件,再进行处理。

   

  

   

  

   

  二、             WEB SERVER

   

  方案中WEB SERVER的作用主要是根据ACTIVEX的请求调用REPORT SERVER,并将REPORT SERVER生成的.zip文件发送给ACTIVEX。样例通过一个report.jsp文件来处理:ACTIVEX通过get请求report.jsp文件,report.jsp文件调用REPORT SERVER处理后,将.zip文件发送给ACTIVEX。

   

  Report.jsp文件:

   

  %@ page import="…"%

   

  %@page contentType=" APPLICATION/OCTET-STREAM" %

   

  %

   

      try

   

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

      {

   

          String reqFileName = request.getParameter("rpFileName");

   

  String reqMode = request.getParameter("mode");//d为设计报表,r为打印报表

   

  String rpFileName = xxxx.getRpFileName(reqFileName); //根据请求的报表名获得实际的报表文件名,如请求订单报表,而订单报表实际对应的报表文件为order.frf。

   

          String      l_cmd="reportserver.exe "+reqMode+" "+ reqFileName;

   

          Process l_ps=java.lang.Runtime.getRuntime().exec(l_cmd,null);

   

          byte[] l_b=new byte[100];

   

          while(l_ps.getInputStream().read(l_b,0,100)!=-1){

   

                 ;

   

          }

   

         

   

  //发送文件

   

  String zipFileName = xxxx.getZipFileName(reqFileName); //获得压缩文件名

   

  response.setHeader("Content-Disposition","attachment; filename="" +

   

  zipFileName + """);

   

      java.io.FileInputStream fileInputStream =

   

  new java.io.FileInputStream(zipFileName);

   

      int i;

   

      while ((i=fileInputStream.read()) != -1) {

   

       out.write(i);

   

      }

   

      fileInputStream.close();

   

      out.close();

   

      }

   

      catch(Exception e)

   

      {

   

          ……

   

      }

   

  %

   

  String l_cmd="reportserver.exe "+reqMode+" "+ reqFileName; 组成调用REPORT SERVER的命令串。while(l_ps.getInputStream().read(l_b,0,100)!=-1){ ; } 等待REPORT SERVER执行完成,否则,程序在启动REPORT SERVER后即执行下一行语句。发送文件的方式有多种,比如也可以由ACTIVEX通过ftp方式取得。

   

  

   

  

   

  

   

  三、ACTIVEX

   

  方案中的ACTIVEX控件主要做两方面的事情,一是报表利用FASTREPORT控件进行报表处理,包括报表设计(.frf文件)和报表打印(.frp文件)。一是与WEB SERVER进行通信,请求和接收包文件。

   

  DELPHI中NEW一个ActiveForm 应用,取名为reportAForm。在form中加入Combox、button、edit、label等与用户交互的控件;为了处理报表,加入FASTREPORT的多个frSpeedButton用于处理报表事件,如设计、预览、打印、翻页、保存等;加入frReport、frDBDataSet、frDesigner等用于在运行时设计报表;如果设计报表时要使用图形、复选框等内容,也要加入相应的控件;加入frPreview、frTextExport、frRTFExport等控件使可以预览报表并可以将报表输出为text、rtf等格式文件;加入ADOQuery(根据实际需要可加入多个)为报表设计提供数据环境,ADOQuery不OPEN,不与数据库连接;加入NMHTTP用于与WEB SERVER联系。加入四个函数:DesignReport(rpFileName:String),PrintReport(rpFileName:String),unzipReportFiles(rpFileName:String),getReportFile(rpFileName,mode:String)分别用于设计报表、打印报表、解压缩报表和向WEB SERVER发送请求以取得报表文件 。

   

  getReportFile(rpFileName,mode:String)方法:

   

  function TreportAForm.getReportFile(rpFileName,mode:String):boolean;

   

  var

   

  ……

   

          zipFileName:String;

   

  begin

   

      ……

   

  zipFileName:=StringReplace(rpFileName, ExtractFileExt(rpFileName),'.zip',

   

  [rfReplaceAll, rfIgnoreCase]);

   

  try

   

          NMHTTP1.inputFileMode := TRUE;

   

          NMHTTP1.body:='. '+ zipFileName;

   

  NMHTTP1.Get('http://www…./../report.jsp?rpFileName='+

   

  rpFileName+'&mode='+mode);

   

  Result:=true;

   

  except

   

          on Exception do

   

          Result:=false;

   

          end;

   

  end;

   

  函数getReportFile的作用是向WEB SERVER发送报表请求(通过NMHTTP的Get方法),并将返回的压缩包文件保存到本地硬盘(zipFileName)。

   

  unzipReportFiles(rpFileName:String)方法:

   

  function TreportAForm.unzipReportFiles(rpFileName:String) :boolean;

   

  var

   

  ……

   

          zipFileName,fileName:String;

   

          zipCount:Integer;

   

  begin

   

          ……

   

          zipFileName:=StringReplace(rpFileName, ExtractFileExt(rpFileName),'.zip',

   

  [rfReplaceAll, rfIgnoreCase]);

   

          fileName:= ExtractFileName(rpFileName);

   

          fileName:= ChangeFileExt(fileName,'.*');

   

          try

   

                  VCLUnZip1.ZipName:= '.'+ zipFileName;

   

                  VCLUnZip1.DestDir:= '.';

   

                  VCLUnZip1.OverwriteMode:= Always;

   

                  VCLUnZip1.ReadZip;

   

                  VCLUnZip1.FilesList.Add(fileName);

   

                  zipCount:= VCLUnZip1.UnZip;

   

                  if zipCount = 0 then

   

                          result:=false

   

                  else

   

                          result:=true; 

   

          except

   

              on Exception do

   

                  result:=false;

   

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

          end;

   

  end;

   

  函数unzipReportFiles的作用是将压缩包中的文件解压出来,供ACTIVEX使用。它与REPORT SERVER程序中的zipReportFiles刚好是个相反的过程。

   

  DesignReport(rpFileName:String)方法:

   

  function TreportAForm. DesignReport (rpFileName:String) :boolean;

   

  var

   

          dtfFileName,rpFileName:String;

   

          fldlist:TStringList;

   

          T: TStringField;

   

          i:Integer;

   

  begin

   

          ……

   

          dtfFileName:=StringReplace(rpFileName, ExtractFileExt(rpFileName),'.dtf',

   

  [rfReplaceAll, rfIgnoreCase]);//获得数据环境文件名

   

          fldlist:=TStringList.Create;

   

          fldlist.LoadFromFile(dtfFileName);

   

          rpAdoquery.Fields.Clear;

   

          for i := 0 to fldlist.Count - 1 do

   

          begin

   

                  T := TStringField.Create(nil);

   

                  T.FieldName := fldlist[i];

   

                  T.Name := rpAdoquery.Name + T.FieldName;

   

                  rpAdoquery.Fields.add(T);

   

          end;

   

          FrReport1.LoadFromFile(rpFileName);

   

          FrReport1.DesignReport;

   

  end;

   

  函数DesignReport先从.dtf(由REPORT SERVER生成)文件中恢复报表的数据环境,接着使用FASTREPORT的FrReport控件设计报表。在FASTREPORT中,对DataSet中的Field只关心名称(全部通过Variant类型处理),而并不关心数据类型,所以恢复报表的数据环境时,所有字段都当作String类型加入。样例假设报表只有一个名为rpAdoquery的DataSet。报表设计运行时窗口在ACTIVEX进程空间运行。

   

  用户端设计好报表并保存后,需要将保存的报表文件(.frf)回送给服务器存储。文件上传对于大部分开发人员来说应该都是熟悉而简单的,该部分程序本文就省略了。

   

  PrintReport(rpFileName:String)方法:

   

  function TreportAForm. PrintReport (rpFileName:String) :boolean;

   

  var

   

          repFileName:String;

   

  begin

   

          ……

   

          repFileName:=StringReplace(rpFileName, ExtractFileExt(rpFileName),'.frp',

   

                  [rfReplaceAll, rfIgnoreCase]);//获得已准备的报表文件名

   

          try

   

                  frPreview1.clear;

   

                  FrReport1.Preview:=nil;

   

                  FrReport1.clear;

   

                  FrReport1.LoadPreparedReport(repFileName);

   

                  FrReport1.Preview :=frPreview1;

   

                  FrReport1.ShowPreparedReport;

   

                  result:=true;

   

          except

   

              on Exception do

   

                  result:=false;

   

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

          end;

   

  end;

   

  函数PrintReport装入由REPORT SERVER运行好的报表.frp文件,通过调用FrReport的ShowPreparedReport方法在ACTIVEX端预览和打印。

   

方案实现方法的介绍结束。本方案具有的优点为:保持应用的结构形式不变(B/S),将C/S应用结构下已非常成熟的报表方案移植过来,使得在WEB应用中也可实现任意复杂的报表设计和打印,以及对打印效果进行精确控制。
展开更多 50%)
分享

猜你喜欢

用FASTREPORT实现WEB应用中自定义报表

编程语言 网络编程
用FASTREPORT实现WEB应用中自定义报表

用javascript实现自定义标签

Web开发
用javascript实现自定义标签

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

在WEB自定义控件中实现事件及自动保存值

电脑网络
在WEB自定义控件中实现事件及自动保存值

PowerPoint借助自定义形状实现填充自定义图片

电脑网络
PowerPoint借助自定义形状实现填充自定义图片

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

java Lucene 中自定义排序的实现

Web开发
java Lucene 中自定义排序的实现

自定义快速报表的打印预览窗口

编程语言 网络编程
自定义快速报表的打印预览窗口

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

网页的栅格设计思考

网页的栅格设计思考

数据结构C语言实现系列——队列

数据结构C语言实现系列——队列
下拉加载更多内容 ↓