| 導購 | 订阅 | 在线投稿
分享
 
 
當前位置: 王朝網路 >> delphi >> 用FASTREPORT實現WEB應用中自定義報表
 

用FASTREPORT實現WEB應用中自定義報表

2006-12-17 07:43:48  編輯來源:互聯網  简体版  手機版  評論  字體: ||
 
 
  用FASTREPORT實現WEB應用中自定義報表

  

  用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 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應用中也可實現任意複雜的報表設計和打印,以及對打印效果進行精確控制。
 
 
 
上一篇《創建網站的 RSS 內容摘要》
下一篇《將指定字符串後面的"0"字符截去[原創]》
 
 
 
 
 
 
日版寵物情人插曲《Winding Road》歌詞

日版寵物情人2017的插曲,很帶節奏感,日語的,女生唱的。 最後聽見是在第8集的時候女主手割傷了,然後男主用嘴幫她吸了一下,插曲就出來了。 歌手:Def...

兄弟共妻,我成了他們夜裏的美食

老鍾家的兩個兒子很特別,就是跟其他的人不太一樣,魔一般的執著。兄弟倆都到了要結婚的年齡了,不管自家老爹怎麽磨破嘴皮子,兄弟倆說不娶就不娶,老父母爲兄弟兩操碎了心...

如何磨出破洞牛仔褲?牛仔褲怎麽剪破洞?

把牛仔褲磨出有線的破洞 1、具體工具就是磨腳石,下面墊一個硬物,然後用磨腳石一直磨一直磨,到把那塊磨薄了,用手撕開就好了。出來的洞啊很自然的。需要貓須的話調幾...

我就是掃描下圖得到了敬業福和愛國福

先來看下敬業福和愛國福 今年春節,支付寶再次推出了“五福紅包”活動,表示要“把欠大家的敬業福都還給大家”。 今天該活動正式啓動,和去年一樣,需要收集“五福”...

冰箱異味産生的原因和臭味去除的方法

有時候我們打開冰箱就會聞到一股異味,冰箱裏的這種異味是因爲一些物質發出的氣味的混合體,聞起來讓人惡心。 産生這些異味的主要原因有以下幾點。 1、很多人有這種習...

《極品家丁》1-31集大結局分集劇情介紹

簡介 《極品家丁》講述了現代白領林晚榮無意回到古代金陵,並追隨蕭二小姐化名“林三”進入蕭府,不料卻陰差陽錯上演了一出低級家丁拼搏上位的“林三升職記”。...

李溪芮《極品家丁》片尾曲《你就是我最愛的寶寶》歌詞

你就是我最愛的寶寶 - 李溪芮 (電視劇《極品家丁》片尾曲) 作詞:常馨內 作曲:常馨內 你的眉 又鬼馬的挑 你的嘴 又壞壞的笑 上一秒吵鬧 下...

烏梅的功效與作用以及烏梅的食用禁忌有哪些?

烏梅,又稱春梅,中醫認爲,烏梅味酸,性溫,無毒,具有安心、除熱、下氣、祛痰、止渴調中、殺蟲的功效,治肢體痛、肺痨病。烏梅泡水喝能治傷寒煩熱、止吐瀉,與幹姜一起制...

什麽是脂肪粒?如何消除臉部脂肪粒?

什麽是脂肪粒 在我們的臉上總會長一個個像脂肪的小顆粒,弄也弄不掉,而且顔色還是白白的。它既不是粉刺也不是其他的任何痘痘,它就是脂肪粒。 脂肪粒雖然也是由油脂...

網絡安全治理:國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰受害者

來源:中國青年報 新的攻擊方法不斷湧現,黑客幾乎永遠占據網絡攻擊的上風,我們不可能通過技術手段杜絕網絡攻擊。國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰...

河南夫妻在溫嶺網絡直播“造人”內容涉黃被刑事拘留

夫妻網絡直播“造人”爆紅   1月9日,溫嶺城北派出所接到南京警方的協查通告,他們近期打掉了一個涉黃直播APP平台。而根據掌握的線索,其中有一對涉案的夫妻主播...

如何防止牆紙老化?牆紙變舊變黃怎麽辦?

如何防止牆紙老化? (1)選擇透氣性好的牆紙 市場上牆紙的材質分無紡布的、木纖維的、PVC的、玻璃纖維基材的、布面的等,相對而言,PVC材質的牆紙最不透氣...

鮮肌之謎非日本生産VS鮮肌之謎假日貨是謠言

觀點一:破日本銷售量的“鮮肌之謎” 非日本生産 近一段時間,淘寶上架了一款名爲“鮮肌之謎的” 鲑魚卵巢美容液,號稱是最近日本的一款推出的全新護膚品,産品本身所...

中國最美古詩詞精選摘抄

系腰裙(北宋詞人 張先) 惜霜蟾照夜雲天,朦胧影、畫勾闌。人情縱似長情月,算一年年。又能得、幾番圓。 欲寄西江題葉字,流不到、五亭前。東池始有荷新綠,尚小如...

關于女人的經典語句

關于女人的經典語句1、【做一個獨立的女人】 思想獨立:有主見、有自己的人生觀、價值觀。有上進心,永遠不放棄自己的理想,做一份自己喜愛的事業,擁有快樂和成就...

未來我們可以和性愛機器人結婚嗎?

你想體驗機器人性愛嗎?你想和性愛機器人結婚嗎?如果你想,機器人有拒絕你的權利嗎? 近日,第二屆“國際人類-機器人性愛研討會”大會在倫敦金史密斯大學落下帷幕。而...

全球最變態的十個地方

10.土耳其地下洞穴城市 變態指數:★★☆☆☆ 這是土耳其卡帕多西亞的一個著名景點,傳說是當年基督教徒們爲了躲避戰爭而在此修建。裏面曾住著20000人,...

科學家稱,人類死亡後意識將在另外一個宇宙中繼續存活

據英國《每日快報》報道,一位科學家兼理論家Robert Lanza博士宣稱,世界上並不存在人類死亡,死亡的只是身體。他認爲我們的意識借助我們體內的能量生存,而且...

《屏裏狐》片頭曲《我愛狐狸精》歌詞是什麽?

《我愛狐狸精》 - 劉馨棋   (電視劇《屏裏狐》主題曲)   作詞:金十三&李旦   作曲:劉嘉   狐狸精 狐狸仙   千年修...

 
 
 
用FASTREPORT實現WEB應用中自定義報表 用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 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應用中也可實現任意複雜的報表設計和打印,以及對打印效果進行精確控制。
󰈣󰈤
 
 
 
  免責聲明:本文僅代表作者個人觀點,與王朝網路無關。王朝網路登載此文出於傳遞更多信息之目的,並不意味著贊同其觀點或證實其描述,其原創性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容、文字的真實性、完整性、及時性本站不作任何保證或承諾,請讀者僅作參考,並請自行核實相關內容。
 
 
陽光靓麗的模特兒(8)
陽光靓麗的模特兒(7)
陽光靓麗的模特兒(6)
陽光靓麗的模特兒(5)
秋-印象
德慶盤龍峽 一
松江印象之三
雲之南(寬幅)
 
>>返回首頁<<
 
 
 
 熱帖排行
 
 
 
 
© 2005- 王朝網路 版權所有