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

来源:互联网 发布:海鸥手表 知乎 编辑:程序博客网 时间:2024/05/11 04:07

用fastreport实现web应用中自定义报表_delphi教程

研发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 paramCount>1 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;

        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;

        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;

        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

    {

        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;

        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;

        end;

end;

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

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


转载:http://blog.163.com/xu_shuhao/blog/static/525774872011102485032673/


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 没收到货退款商家不处理怎么办 没收到货申请退款卖家不处理怎么办 京东已收到货却不处理退款怎么办 货退了卖家不退款怎么办 淘宝退了货卖家不退款怎么办 手机淘宝不显示图片怎么办 京东申请退款卖家不处理怎么办 企业网银冻结了怎么办 农行k宝坏了怎么办 手机检测不到u盾怎么办 农行有k宝怎么办信用卡 k米怎么点不了怎么办 c1驾照被扣3分怎么办 淘宝账号被黑了怎么办 淘宝卖家号虚假交易违规怎么办 扣扣申诉成功后怎么办 微信二维码收款异常怎么办 国际包裹被退回去了怎么办 京东账号手机号换了怎么办 换手机号了淘宝账号怎么办 qq登录id密码忘记怎么办 iphone商店密码忘记了怎么办 淘宝账号被限制登入怎么办 手机换号码了qq登不上怎么办 换手机了qq登不上怎么办 微信帐号和密码错误怎么办 高考生忘记登录密码怎么办 高考生登录密码丢了怎么办 高考志愿登录密码忘了怎么办 电视声音和画面不同步怎么办 苹果5s不能开机怎么办 红米手机老是闪退怎么办 苹果7plus打字卡怎么办 手机总是出现无响应怎么办 手机淘宝怎么打不开了怎么办 淘宝买东西卖家不同意退货怎么办 苹果自带浏览器不能上网怎么办 淘宝账号买不了东西怎么办 支付宝被限制登录怎么办 微信登录不上 钱怎么办 淘宝账号买家权限被限制怎么办