国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

用FASTREPORT實現(xiàn)WEB應(yīng)用中自定義報表

2019-11-18 17:59:20
字體:
供稿:網(wǎng)友

開發(fā)WEB應(yīng)用系統(tǒng)通常都會遇到報表打印問題。簡單應(yīng)用可利用IE的頁面打印功能,利用HTML標簽控制格式來實現(xiàn)。但復(fù)雜的業(yè)務(wù)型應(yīng)用系統(tǒng),報表不僅是組成應(yīng)用的重要部分,還常常是相當復(fù)雜的。現(xiàn)在很多應(yīng)用系統(tǒng)都要求提供自定義報表的功能——即客戶可以自行設(shè)計、修改報表。

 

C/S結(jié)構(gòu)系統(tǒng)中,報表問題有很多成熟的解決方法。如DELPHI開發(fā)工具不僅自帶有報表控件,還可以利用第三方控件來實現(xiàn)快速靈活的報表制作和打印,其中有名的控件是FR-Software & A.Tzyganenko 的FastReport。FastReport提供了能與DELPHI無縫集成的從設(shè)計到打印的完整控件包,提供的設(shè)計界面友好靈活,對于開發(fā)可讓用戶自定義報表的C/S應(yīng)用來說,是一種很好的解決方式。

 

B/S結(jié)構(gòu)應(yīng)用中,Crystal Report是一種大型報表系統(tǒng)常用和推薦的解決方案。但Crystal Report目前價格昂貴,而且該系統(tǒng)相當龐大。它的可定制性及精確控制打印效果方面尚不夠完善。當然,在目前市場上,它仍是一種首選的WEB應(yīng)用的報表解決方案。

 

如果能將C/S應(yīng)用中成熟的報表解決方案搬到B/S應(yīng)用中,相信對于大部分開發(fā)人員來說,都是非常歡迎的。本文將講述一個在java環(huán)境中利用FastReport實現(xiàn)B/S應(yīng)用中用戶可自定義的報表解決方案。因為筆者近段時間正用DELPHI、JAVA做一些項目,所以樣例代碼就以DELPHI、JAVA編寫。

 

本解決方案樣例的基本環(huán)境是:WINDOWS 2000 SERVER+SQL SERVER 2000+TOMCAT 4.0。開發(fā)工具:IntelliJ IDEA 3.0,DELPHI 5.0。客戶端為IE 5.0瀏覽器。

 

方案共要求用DELPHI編寫兩個程序,一個是將被包含在網(wǎng)頁中并在瀏覽器中運行的ACTIVEX(.ocx),一個是運行在服務(wù)器端的報表處理程序,中間通過JAVA程序連接——或任何其他WEB語言都可以,如aspphp等。方案的基本原理圖如下:

 

報表設(shè)計過程

 

報表打印過程

 

REPORT SERVER:可以做成一個普通的WINDOWS程序,也可以做成一個COM程序(Automation Object)。在報表設(shè)計過程中,用戶端(ACTIVEX)向WEB SERVER發(fā)送報表設(shè)計請求,請求中包含要設(shè)計報表的名稱;WEB SERVER 收到該請求后,調(diào)用REPORT SERVER請求設(shè)計的報表文件;REPORT SERVER收到請求后,先裝載報表的數(shù)據(jù)環(huán)境,然后將報表設(shè)計文件(.frf)和該報表的數(shù)據(jù)環(huán)境文件壓縮成一個包文件(.zip),將該包文件的完整路徑名返回給WEB SERVER調(diào)用程序;WEB SERVER將包文件回送給用戶端(ACTIVEX),用戶端將接收到的包文件保存到本地硬盤上,并解壓縮,從數(shù)據(jù)環(huán)境文件中恢復(fù)數(shù)據(jù)環(huán)境,通過FASTREPORT的相應(yīng)控件打開報表文件給用戶提供可視化設(shè)計。用戶在ACTIVEX中設(shè)計報表時,雖然不能和數(shù)據(jù)庫連接,但因數(shù)據(jù)環(huán)境已存在,所以用戶仿如在通常的C/S應(yīng)用結(jié)構(gòu)下設(shè)計報表,能正常地看到報表的數(shù)據(jù)字典信息。在報表打印過程中,用戶端(ACTIVEX)向WEB SERVER發(fā)送報表打印/預(yù)覽請求,請求中包含要打印/預(yù)覽的報表名稱;WEB SERVER 收到該請求后,調(diào)用REPORT SERVER請求打印或預(yù)覽的報表文件;REPORT SERVER收到請求后,先裝載報表的數(shù)據(jù)環(huán)境,然后裝載報表文件(.frf),接著在無界面狀態(tài)下運行報表,最后將生成的已準備的報表文件(.frp)壓縮成一個包文件(.zip),將該包文件的完整路徑名返回給WEB SERVER調(diào)用程序;WEB SERVER將包文件回送給用戶端(ACTIVEX),用戶端將接收到的包文件保存到本地硬盤上,并解壓縮,通過FASTREPORT的相應(yīng)控件打開報表文件(.frp)給用戶預(yù)覽或打印或重新調(diào)整格式或輸出為其他格式文件。用戶在ACTIVEX中預(yù)覽報表,仿如在通常的C/S應(yīng)用結(jié)構(gòu)下預(yù)覽報表。

 

WEB SERVER:提供通常的WEB服務(wù)功能。

 

ACTIVEX:ActiveX是Microsoft提出的一組使用COM(Component Object Model,部件對象模型)使得軟件部件可在網(wǎng)絡(luò)環(huán)境中進行交互的技術(shù)集。它與具體的編程語言無關(guān)。作為針對Internet應(yīng)用開發(fā)的技術(shù),ActiveX被廣泛應(yīng)用于WEB服務(wù)器以及客戶端的各個方面。本方案中的ACTIVEX控件主要做兩方面的事情,一是利用FASTREPORT控件進行報表處理,包括報表設(shè)計(.frf文件)和報表打印(.frp文件)。一是與WEB SERVER進行通信,請求和接收包文件。本文樣例的ACTIVEX采用DELPHI 5.0編寫。

 

下面分述各部分的一例具體實現(xiàn)(因為僅為說明方案的實現(xiàn),所以很多代碼細節(jié)都進行了簡省)。

 

一、             REPORT SERVER

 

REPORT SERVER既可以做成一個普通的WINDOWS程序,也可以做成一個COM程序(Automation Object)。本例中為簡化見,采用普通的WINDOWS程序?qū)崿F(xiàn)。

 

DELPHI中NEW一個應(yīng)用程序。在FORM中加入TfrReport、TfrDBDataSet、ADOConnection、TADOQuery等控件——為了使用FASTREPORT的控件,需要安裝該控件包,可從站點http://www.fast-report.com 下載,國內(nèi)很多軟件站點都提供該控件包的下載。其中TfrDBDataSet、TADOQuery控件視應(yīng)用需要可加入多個,另外為了壓縮文件,還要加入一個壓縮控件,本例使用VCLZip。在Form1中加入三個函數(shù):PReDesignReport(rpFileName:String),prePrintReport(rpFileName:String),zipReportFiles(rpFileName:String),分別用于準備報表設(shè)計文件、準備報表打印文件、壓縮報表文件 。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   //設(shè)計報表

 

                        if preDesignReport(rpFileName) then

 

                                zipReportFiles(rpFileName);

 

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

 

                        if prePrintReport(rpFileName) then

 

                                zipReportFiles(rpFileName);

 

        end;

 

        application.Terminate;

 

end;

 

程序根據(jù)調(diào)用參數(shù)判斷是準備報表設(shè)計文件還是準備報表打印文件,接著調(diào)用相應(yīng)的過程來實現(xiàn)。最后的Application.Terminate 是讓程序執(zhí)行功能后即退出——因為這是服務(wù)端程序,是不能與用戶交互的。

 

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;//打開報表的數(shù)據(jù)環(huán)境

 

                rpAdoquery.FieldList.SaveToFile(dtfFileName);

 

                result:=true;

 

        except

 

            on Exception do

 

                result:=false;

 

        end;

 

end;

 

函數(shù)preDesignReport的作用是準備報表設(shè)計文件。報表中可以引用多個DataSet,本例假設(shè)報表只引用一個名為rpAdoquery的DataSet。rpFileName 為報表文件名(.frf),DtfFileName為保存數(shù)據(jù)環(huán)境的文件名(.dtf)。因為用戶端不能連接數(shù)據(jù)庫,所以將DataSet中的Fileds通過rpAdoquery.FieldList.SaveToFile(dtfFileName)保存到文件,和報表文件一起傳送給用戶端的ACTIVEX,ACTIVEX利用.dtf文件復(fù)現(xiàn)報表的數(shù)據(jù)環(huán)境。

 

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;//打開報表的數(shù)據(jù)環(huán)境

 

                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;

 

函數(shù)prePrintReport的作用是準備打印的報表文件,即先在服務(wù)器端裝載報表并運行,將運行好的報表保存為文件,用于傳送到用戶端進行預(yù)覽或打印。RepFileName是已準備好的報表文件名(.frp)。同樣假設(shè)報表只引用一個名為rpAdoquery的DataSet。frReport1.ShowProgress:=False 使報表運行過程中不顯示進度窗口(服務(wù)器端不能顯示與用戶交互的界面);接下來frReport1.Clear;…裝載報表文件及設(shè)置相關(guān)數(shù)據(jù)屬性;frReport1.PrepareReport 是在不顯示預(yù)覽窗口的情況下運行報表;frReport1.SavePreparedReport(repFileName) 將運行好的報表保存到文件,該文件傳送給用戶端的ACTIVEX,ACTIVEX可以直接預(yù)覽或顯示該報表。

 

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;

 

函數(shù)zipReportFiles的作用是把要傳送給用戶端的報表文件壓縮為一個.zip文件,簡化文件傳送過程,而且壓縮了數(shù)據(jù)量。ACTIVEX接收到.zip文件后,先解壓出包中文件,再進行處理。

 

 

 

二、             WEB SERVER

 

方案中WEB SERVER的作用主要是根據(jù)ACTIVEX的請求調(diào)用REPORT SERVER,并將REPORT SERVER生成的.zip文件發(fā)送給ACTIVEX。樣例通過一個report.jsp文件來處理:ACTIVEX通過get請求report.jsp文件,report.jsp文件調(diào)用REPORT SERVER處理后,將.zip文件發(fā)送給ACTIVEX。

 

Report.jsp文件:

 

<%@ page import="…"%>

 

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

 

<%

 

    try

 

    {

 

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

 

String reqMode = request.getParameter("mode");//d為設(shè)計報表,r為打印報表

 

String rpFileName = xxxx.getRpFileName(reqFileName); //根據(jù)請求的報表名獲得實際的報表文件名,如請求訂單報表,而訂單報表實際對應(yīng)的報表文件為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){

 

               ;

 

        }

 

       

 

//發(fā)送文件

 

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; 組成調(diào)用REPORT SERVER的命令串。while(l_ps.getInputStream().read(l_b,0,100)!=-1){ ; } 等待REPORT SERVER執(zhí)行完成,否則,程序在啟動REPORT SERVER后即執(zhí)行下一行語句。發(fā)送文件的方式有多種,比如也可以由ACTIVEX通過ftp方式取得。

 

 

 

 

三、ACTIVEX

 

方案中的ACTIVEX控件主要做兩方面的事情,一是報表利用FASTREPORT控件進行報表處理,包括報表設(shè)計(.frf文件)和報表打印(.frp文件)。一是與WEB SERVER進行通信,請求和接收包文件。

 

DELPHI中NEW一個ActiveForm 應(yīng)用,取名為reportAForm。在form中加入Combox、button、edit、label等與用戶交互的控件;為了處理報表,加入FASTREPORT的多個frSpeedButton用于處理報表事件,如設(shè)計、預(yù)覽、打印、翻頁、保存等;加入frReport、frDBDataSet、frDesigner等用于在運行時設(shè)計報表;如果設(shè)計報表時要使用圖形、復(fù)選框等內(nèi)容,也要加入相應(yīng)的控件;加入frPreview、frTextExport、frRTFExport等控件使可以預(yù)覽報表并可以將報表輸出為text、rtf等格式文件;加入ADOQuery(根據(jù)實際需要可加入多個)為報表設(shè)計提供數(shù)據(jù)環(huán)境,ADOQuery不OPEN,不與數(shù)據(jù)庫連接;加入NMHTTP用于與WEB SERVER聯(lián)系。加入四個函數(shù):DesignReport(rpFileName:String),PrintReport(rpFileName:String),unzipReportFiles(rpFileName:String),getReportFile(rpFileName,mode:String)分別用于設(shè)計報表、打印報表、解壓縮報表和向WEB SERVER發(fā)送請求以取得報表文件 。

 

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;

 

函數(shù)getReportFile的作用是向WEB SERVER發(fā)送報表請求(通過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;

 

函數(shù)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]);//獲得數(shù)據(jù)環(huán)境文件名

 

        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;

 

函數(shù)DesignReport先從.dtf(由REPORT SERVER生成)文件中恢復(fù)報表的數(shù)據(jù)環(huán)境,接著使用FASTREPORT的FrReport控件設(shè)計報表。在FASTREPORT中,對DataSet中的Field只關(guān)心名稱(全部通過Variant類型處理),而并不關(guān)心數(shù)據(jù)類型,所以恢復(fù)報表的數(shù)據(jù)環(huán)境時,所有字段都當作String類型加入。樣例假設(shè)報表只有一個名為rpAdoquery的DataSet。報表設(shè)計運行時窗口在ACTIVEX進程空間運行。

 

用戶端設(shè)計好報表并保存后,需要將保存的報表文件(.frf)回送給服務(wù)器存儲。文件上傳對于大部分開發(fā)人員來說應(yīng)該都是熟悉而簡單的,該部分程序本文就省略了。

 

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;

 

函數(shù)PrintReport裝入由REPORT SERVER運行好的報表.frp文件,通過調(diào)用FrReport的ShowPreparedReport方法在ACTIVEX端預(yù)覽和打印。

 

方案實現(xiàn)方法的介紹結(jié)束。本方案具有的優(yōu)點為:保持應(yīng)用的結(jié)構(gòu)形式不變(B/S),將C/S應(yīng)用結(jié)構(gòu)下已非常成熟的報表方案移植過來,使得在WEB應(yīng)用中也可實現(xiàn)任意復(fù)雜的報表設(shè)計和打印,以及對打印效果進行精確控制。

上一篇:序列化FastReport

下一篇:FastReport問題集

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
學(xué)習(xí)交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網(wǎng)友關(guān)注

主站蜘蛛池模板: 文登市| 鹤庆县| 连州市| 五家渠市| 肇源县| 平利县| 邵阳市| 修文县| 类乌齐县| 寿宁县| 大埔县| 都兰县| 桦甸市| 新密市| 建昌县| 密山市| 大荔县| 信阳市| 承德市| 文登市| 洪江市| 桓仁| 长葛市| 广河县| 利辛县| 昂仁县| 思茅市| 扶余县| 独山县| 德惠市| 济源市| 烟台市| 南召县| 浙江省| 东乌| 昭平县| 敖汉旗| 涟源市| 佛冈县| 平江县| 马边|