後臺主要工作是解析XML定義的標籤文件,並獲取到數據集,放入到Map中,然後調用Jacob.jar中提供的相關方法來實現替換。首先想多說一句就是jacob會每次生成word報告時都會啓動一個office word進程,替換完畢之後 需要關閉掉這個進程,如果有死掉的word進程有可能會影響word的生成。這些具體調試或運行過程中就會發現這個問題的。
還需要說明一點,解析XML有很多種方式,自己挑選自己熟悉的就行了,我採用jdom,當時想的沒有這麼負責,所以寫代碼也沒有怎麼重構,需要重新整理,主要方法我會貼出來。
那麼我就從頭到尾的說一下,調用生成報告時,後臺的整個調用過程是怎麼樣的,是如何運轉的?
首先訪問web應用地址:http://192.16.3.22/demo/DocInfo!createDoc.action 這樣我提交一個方法 action方法,這個方法首先是在web應用服務器上的,然後進入action中的createDoc方法,同時你需要獲取到從方法傳過來的相關參數,比如:sql中定義的那個查詢條件,報告類型等參數。
(在去調用生成報告的方法中,可能你還需要加入一些判斷,如是否已經生成過報告啊,或者最新報告的版本啊,因爲我們都是既保存word報告文檔又會在數據庫中插入一條記錄,方便查詢),這樣就開始了:
首先是action的createDoc方法:
/**
* 通過HttpCient調用報告服務器的方法生成報告 DOC
*/
public String createDoc() throws Exception {
//定義放回成功與否的判斷碼
String prMsg="";
// 獲取當前登錄的用戶
UserVo userVo = CommonUtils.getUserMessage();
//獲取模版類型
docType = Struts2Utils.getParameter("docType");
//重新創建文檔
String creatOrnot = Struts2Utils.getParameter("creatOrnot");
//獲取組組編號參數
workgroupId = Struts2Utils.getParameter("workgroupId");
//獲取評估用例實例ID參數
evtcaseInstId = Struts2Utils.getParameter("evtcaseInstId");
if(CommonUtils.isNotNull(docType)){
//獲取項目Id
projectId = Struts2Utils.getParameter("projectId");
if(!CommonUtils.isNotNull(projectId)){
if(CommonUtils.isNotNull(this.getIdFromSession("PM_PROJECTID"))){
projectId = this.getIdFromSession("PM_PROJECTID").toString();
}else{
Struts2Utils.getRequest().setAttribute("msg", "請先選擇項目!");
}
}
if(CommonUtils.isNotNull(projectId)){
prMsg = infoSystemDescService.downloadFileByUrl(projectId, userVo.getUserId(), workgroupId, evtcaseInstId, docType, creatOrnot);
}
}
return "docList";
}
注:在我貼出來的代碼中,能看懂就行了,有些不用管他(可能是其他業務方面的判斷),關於最後返回的prMsg---代表各種狀態 主要表示成功與否或者是出錯的信息。
接着我貼出service層的方法downloadFileByUrl
/**
* 功能:
* 1.(生成報告文檔)
* 2.保存指定URL的源文件到指定路徑下
* @param projectId
* @param userId
* @param workgroupId
* @param evtcaseInstId
* @param docType
* @param creatOrnot
* @return
* @throws Exception
*/
@SuppressWarnings("deprecation")
public synchronized String downloadFileByUrl(String projectId,String userId,String workgroupId,String evtcaseInstId,String docType,String creatOrnot) throws Exception {
String msg = "1";//"1":默認爲創建成功的提示信息 "2":標識創建失敗
String srcUrl = ""; //報告服務器的執行路徑
HttpResponse response = null;
FileOutputStream out = null;
HttpClient httpclient = null;
HttpGet httpget = null;
long time1 = System.currentTimeMillis();
//獲取保存後的路徑
TProjDoc projDoc = projectDocDao.findFileByType(userId, Integer.parseInt(docType), Long.parseLong(projectId), workgroupId,evtcaseInstId);
if(projDoc == null || (projDoc != null && CommonUtils.isNotNull(creatOrnot) && creatOrnot.equals("1"))){ //FT_任務編號_[FID]
try {
//獲取報告服務器的執行路徑
srcUrl = xmlPathDef.getActionUrl(docType, projectId,userId,workgroupId,evtcaseInstId);
HttpParams httpParams = new BasicHttpParams();
// 設置最大連接數
ConnManagerParams.setMaxTotalConnections(httpParams, 1);
// 設置獲取連接的最大等待時間
//ConnManagerParams.setTimeout(httpParams, 6000);
// 設置每個路由最大連接數
ConnPerRouteBean connPerRoute = new ConnPerRouteBean(1);
ConnManagerParams.setMaxConnectionsPerRoute(httpParams,connPerRoute);
// 設置連接超時時間
HttpConnectionParams.setConnectionTimeout(httpParams, 6000);
// 設置讀取超時時間
if(docType.toString().equals(XmlPathDef.SPOTTEST_DOC) && docType.toString().equals(XmlPathDef.FTEST_DOC)){
HttpConnectionParams.setSoTimeout(httpParams, 2400000);
}else{
HttpConnectionParams.setSoTimeout(httpParams, 600000);
}
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(httpParams, registry);
httpclient = new DefaultHttpClient(connectionManager, httpParams);
httpget = new HttpGet(srcUrl);
//執行返回
response = httpclient.execute(httpget);
//如果是本機既當服務器,又當報表服務器,那麼就只生成一遍
String ipvalues = xmlPathDef.getRepUrl();
if(CommonUtils.isNotNull(ipvalues)){
if(ipvalues.indexOf(":") != -1){
ipvalues = ipvalues.substring(0,ipvalues.lastIndexOf(":"));
}
}
HttpEntity entity = response.getEntity();
//獲取保存後的路徑
projDoc = projectDocDao.findFileByType(userId,Integer.parseInt(docType), Long.parseLong(projectId), workgroupId,evtcaseInstId);
String filePath = "";
if(projDoc != null)
filePath = projDoc.getPath();
if(CommonUtils.isNotNull(filePath)){
String basepath = XmlPathDef.getBasePath();
String outFilePath = (basepath + filePath).replaceAll("\\\\", "\\/");
XmlPathDef.isExists(outFilePath);
File wdFile = new File(outFilePath);
out = new FileOutputStream(wdFile);
int l;
byte[] tmp = new byte[2048];
while ((l = instream.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
out.flush();
out.close();
System.out.println("****************************** ");
System.out.println("");
System.out.println("*************** 恭喜! 報告創建成功 結束 ***************");
System.out.println("");
}else{
msg = "8";//說明word創建成功,但是數據沒有保存成功
response = null;
}
}else{
msg = "2";
}
} catch (ClientProtocolException e) {
msg = "7";
e.printStackTrace();
} catch (IOException e) {
msg = "7";
logger.error("數據庫報告服務器地址配置錯誤或網絡不通!!2.連接是否超時" + e.getMessage());
e.printStackTrace();
}finally{
if(out!=null){
try {
out.close();
} catch (IOException e) {
msg = "7";
logger.error("數據庫報告服務器地址配置錯誤或網絡不通!!2.連接是否超時" + e.getMessage());
e.printStackTrace();
}
}
}
}
long time2 = System.currentTimeMillis();
long numTime = time2 - time1;
if(docType.toString().equals(XmlPathDef.SPOTTEST_DOC) && docType.toString().equals(XmlPathDef.FTEST_DOC)){
if(numTime >= 2401000){
msg = "9";
}
}else{
if(numTime >= 601000){
msg = "9";
}
}
System.out.println("");
String loggerinfo = "********* 報告類型爲 :" + docType + " 執行時間爲: " + (time2 - time1) /1000 + " 秒!***************";
System.out.println(loggerinfo);
System.out.println("");
System.out.println("*****************************");
logger.info(loggerinfo);
return msg;
}
這個方法還有待優化和調整,現在我主要說明他的作用:
首先使用了synchronized 關鍵字 意思說使用同步的方式,讓每次只有一個線程運行這個方法(其實如果是大數量的併發,就不能這樣寫了,自己去摸索吧)。然後在方法體中,使用了一個httpclient技術,這個主要是調用遠程的服務返回對象流,在我這裏就是調用另外一個服務器的代碼,去生成報告,
然後將word報告的流返回來就ok,如果不明白httpclient的用法,請上網學一下,具體我就不說了。(下節我會貼出相應的代碼)
最後通過httpclient的返回流寫成word文檔就行了(其實裏面還有一些判斷什麼的,比如:最後返回流是否是word流啊,什麼的 都需要判斷的,我都去掉了,代碼太多),返回一個狀態碼給action就完畢。
(未完待續)