BS系統自動更新的實現


背景:

我公司做的考試系統(基於java開發的BS系統)是賣給學校的,隨着客戶數量增多,日常版本升級、遠程維護工作占了程序員很多時間,遂考慮實現系統自動化更新。


 

要解決的問題及解決方案:

1.什么時候檢查更新及更新策略。

由於系統隨時有可能處於使用狀態,全自動更新容易導致問題,因此是否執行自動更新由系統管理員決定,當系統管理員登錄到后台管理界面時系統會自動檢查更新並提示。管理員點擊觸發執行更新。由於更新的執行依托系統本身的程序,因此不能在更新前關停掉tomcat。也就是只能執行熱更新,但是熱更新也有些問題需要解決,java類的class需要動態裝載,而spring、struts的配置文件由於依賴關系、文件較多,難以實現動態加載,就算實現了,系統session也會被重新初始化,這樣很容易產生錯誤數據,且學生們也必須重新登錄。與其這樣,倒不如更新完直接提示管理員必須重啟服務器后才可繼續使用。

 

2.要支持三種類型的更新:程序補丁包、數據庫更新、特殊數據更新

程序補丁包:日常bug解決,新增或修改的功能,按運行環境目錄結構組織更新文件,打包成zip文件,客戶端下載后解壓覆蓋實現更新。

數據庫更新:將對數據庫的任何操作,制作成sql腳本,客戶端下載sql腳本並調用執行腳本方法實現更新。

特殊數據更新:我們系統是考試系統,有題庫,題數據保存在數據庫中,但也可以導入導出成文件,我們給學校更新題庫時,是給文件進行導入。自動更新就需要實現自動導入。

3.客戶端不應重復更新,而且應確保准確無誤的的執行完整的更新。

有兩個概念要區分:每次更新與每個更新。每次更新是系統管理員不定期的檢查和執行整體更新,每個更新是一次更新中的每個具體更新,每次更新包含多個“每個更新”,因為每次更新可能會多次下載多個文件執行更新。

客戶端每執行完一個更新時,會將本更新的文件單獨留存到客戶端的一個叫last-updated-files的目錄下,下次系統請求遠程服務器檢查更新時會帶上這個文件名,得到大於這個文件名的文件列表(也就是認為還沒更新的),這樣可以防止重復更新,保證按順序更新。

因此,更新包必須加入時間概念來確保更新順序,我采用的是更新包的命名上入手,每個更新包文件命名必須包含日期時間,同類更新包,客戶端按日期時間排序逐個逐個下載並執行更新。

4.如何應對更新失敗及客戶端版本回滾。

基於第3點的機制,更新失敗不會在本地留存更新包文件,因此檢查更新時服務器會傳回所有文件,這樣就可以重復執行這個更新。本方案不考慮直接的版本回滾,實現起來有些麻煩,通過另外的方式解決回滾問題,即如果某次更新包有問題,那么就推出新的更新包,用后來的更新覆蓋之前的更新來解決問題。

 


 

程序構成:

整個自動化更新系統由服務端、客戶端構成。服務端即一個提供更新包的遠程服務器,客戶端即學校安裝的BS系統。

基本原理:下載並解壓zip替換文件,下載執行sql,下載dat文件並更新題庫.

服務端主要包含:

一個包文件夾,更新包以時間命名后都放到包目錄下,名稱規則:201710201433.zip(年月日時分.zip)
一個文件上傳及管理jsp


一個更新檢查jsp
客戶端:
后台主界面添加代碼,管理員登錄后自動ajax請求遠程服務器,帶上本地最后一次更新的文件名(文件名根據不同類型可能是多個),
獲得未更新的文件名列表,如果有新的更新,則提示某類型發現N個新更新,是否立即更新,點更新后,前端挨個挨個請求后台下載更新包,執行更新.
程序文件更新(zip),下載后解壓並覆蓋舊文件;數據庫更新,下載sql文件並執行;試題更新,下載試題執行試題導入功能.
更新時在頁面上顯示更新進度。更新結束要求重啟系統。


 

關鍵程序代碼:

本功能是在一個Struts2、Spring、Hibernate項目中實現的。

服務端:

服務端是一個web應用,主要功能有管理員登錄、文件管理、文件上傳、更新檢查(接收客戶端請求,根據客戶端的參數)。

此處只貼出更新檢查的代碼。

  1 <%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%>
  2 <%
  3 String path = request.getContextPath();
  4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  5 
  6 
  7 /***
  8  *
  9  *
 10  * 私有雲自動更新
 11  *
 12  *
 13  * 自動更新原理:
 14  * 客戶端(私有雲)admin登錄到后台系統后,后台系統會自動從本web獲取還未下載的更新包名稱列表。
 15  * 更細包包含程序更新包(.zip文件)、數據庫更新文件(.sql文件)和題庫更新包(.dat文件),
 16  *
 17  * 補丁包(zip)和數據庫更新(sql)有可能根據時間推移會產生多個版本,他們的文件名前綴直接以時間命名即可。
 18  * 試題比較特殊,同一套試卷原則上只應該有一個對應的更新文件,如果有新的改動,刪除舊的包,取新名上傳。
 19  * 同套試卷也支持上傳多個包,但是更新檢查時只會獲得最新那個包名(按文件名中的日期部分排序)。
 20  * 不同的試題試卷號不同,他們是針對具體內容的更新,因此試卷的命名以“試卷編號_加密狗權限識別碼_年月日時分.dat”格式,
 21  * 舉例:666_yyn_201712051422.dat,
 22  * 釋意:
 23  * 666:試卷編號
 24  * yyn:該題屬於財務核算實訓雲平台版及財務管理實訓雲平台版,不屬於管理信息化實訓雲平台,y=yes,n=no,三個字符分別表示三個版本權限
 25  * 201712051422:本更新包時間點是2017年12月5日14點22分,私有雲檢查更新時同一套試卷會根據此部分值排序得到最大的。
 26  *
 27  * 客戶端(私有雲平台)請求檢查更新時,默認傳來最后一次更新的程序補丁文件名(zip或sql)以及最后一次更新的試題名(多個),
 28  * 本程序根據名稱(日期命名的)與本程序包目錄下的文件進行對比,得出大於該日期的程序文件名(也就是還未被它下載更新過的)。
 29  * 更新后將最后一次更新的包存放到last-update-file目錄,便於下次更新時傳給服務器獲取新列表。
 30  * 同一時間的程序補丁包和數據庫更新文件作為一個整體補丁業務處理,因為它們是相互依存的,同一時間,如果程序和數據庫同時發生了變化,
 31  * 那么它們本來就是一個整體更新,更新包必須保持順序執行,因此它們的區別只是后綴名不同。
 32  *
 33  * 試題的更新,客戶端傳來最后一次更新的試卷名稱(不同的試卷有自己的最后包名)與服務器最新試卷包名(每個試卷都有自己的)進行對比,
 34  * 只要所屬試卷的最后一次更新名稱(日期命名的)小於服務器返回的,就是要下載安裝的。
 35  * 試題更新結束將當前試卷最后一次更新的包放到last-updated-file目錄里,便於下次更新對比。
 36  *
 37  * 更新檢查時服務端按.zip、.sql、.dat分組排序返回,客戶端下載更新時也按分組列表順序。
 38  *
 39  * 注意,如果sql文件內容包含中文,那么文件和內容必須是UTF-8編碼的,否則更新到數據庫會形成亂碼。
 40  *
 41  * 程序組成:
 42  * 分服務端與客戶端,不設計更新前備份與回滾機制,如果某次更新有問題,再出新的更新包執行替換來解決。
 43  * 基本原理:下載並解壓zip替換文件,下載執行sql,下載dat文件並更新題庫.
 44  * 服務端主要包含:
 45  * 一個包文件夾,更新包以時間命名后都放到包目錄下,名稱規則:201710201433.zip(年月日時分.zip)
 46  * 一個文件上傳及管理jsp
 47  * 一個更新檢查jsp
 48  * 客戶端:
 49  * 后台主界面添加代碼,管理員登錄后自動ajax請求遠程服務器,帶上本地最后一次更新的文件名(文件名根據不同類型可能是多個),
 50  * 獲得未更新的文件名列表,如果有新的更新,則提示某類型發現N個新更新,是否立即更新,點更新后,前端挨個挨個請求后台下載更新包,執行更新.
 51  * 程序文件更新(zip),下載后解壓並覆蓋舊文件;數據庫更新,下載sql文件並執行;試題更新,下載試題執行試題導入功能.
 52  * 更新時在頁面上顯示更新進度。更新結束要求重啟系統。
 53  *
 54  *
 55  */
 56 
 57 
 58 
 59 //得到加密狗信息
 60 String encrypt = request.getParameter("encrypt");
 61 if(encrypt==null || "".equals(encrypt) || "0".equals(encrypt)){
 62     
 63     Integer.parseInt("加密狗參數錯誤!");
 64     return;
 65 }
 66 
 67 //取得傳來的最后一次更新的程序文件的名,參數不能帶.zip后綴,因為下面要直接用於比較數字大小。
 68 String lastUpdateFileNames = request.getParameter("lastUpdateFileNames");
 69 
 70 System.out.println("lastUpdateFileNames:"+lastUpdateFileNames);
 71 
 72 String[] lastUpdateFileNamesArray = null;//被以逗號分隔的傳遞來的最后更新的文件名數組。
 73 ArrayList<String> paperNames = new ArrayList<String>();//最后更新的試卷更新包名稱集合
 74 
 75 String lastZipName = "";//最后更新的補丁名
 76 String lastSqlName = "";//最后更新的sql腳本名
 77 if(lastUpdateFileNames!=null && !"".equals(lastUpdateFileNames)){
 78     
 79     if(lastUpdateFileNames.indexOf(",")!=-1){
 80         lastUpdateFileNamesArray = lastUpdateFileNames.split(",");
 81     }else{
 82 
 83         lastUpdateFileNamesArray = new String[]{lastUpdateFileNames};
 84     }
 85     
 86     
 87     for(String ss:lastUpdateFileNamesArray){
 88         String fileName = ss.split("\\.")[0];
 89         String extendName = ss.split("\\.")[1];
 90         if("dat".equals(extendName)){
 91             //將試卷的最后更新的文件名稱添加到集合中,便於下面對比
 92             paperNames.add(ss);
 93         }else if("zip".equals(extendName)){
 94             //最后更新的補丁名稱
 95             lastZipName = ss;
 96         }else if("sql".equals(extendName)){
 97             //最后更新的補丁名稱
 98             lastSqlName = ss;
 99         }
100     }
101 }
102 
103 //本web路徑,用於讀取包文件列表
104 String realPath = request.getRealPath("/files");
105 
106 
107 String result = "";
108 
109 //讀取包文件列表
110 File file = new File(realPath);
111 File[] files=file.listFiles();
112 if(files.length>0){
113     
114     ArrayList<File> fileList = new ArrayList<File>();
115     ArrayList<File> datFileList = new ArrayList<File>();
116     
117     for(File temp:files){
118         String extName = temp.getName().substring(temp.getName().indexOf('.')+1);
119         if("zip".equals(extName) || "sql".equals(extName)){
120             fileList.add(temp);
121         }else if("dat".equals(extName)){
122             //此處還需要根據加密狗過濾無權的試卷(加密狗權限識別碼)。
123             //實訓雲平台加密狗版本號、標識及權限:
124             //0=ER=加密狗加載錯誤
125             //104=HN=財務核算實訓雲平台
126             //105=HO=財務管理實訓雲平台
127             //106=HP=管理信息化實訓雲平台
128             String permissionsDescribedString = temp.getName().split("_")[1];
129             char[] pd = permissionsDescribedString.toCharArray();
130 
131             //財務核算實訓雲平台104對應第一個元素
132             if("104".equals(encrypt) && pd[0]=='y'){
133                 datFileList.add(temp);
134             }else
135 
136             //財務管理實訓雲平台105對應第二個元素
137             if("105".equals(encrypt) && pd[1]=='y'){
138                 datFileList.add(temp);
139             }else
140 
141             //管理信息化實訓雲平台106對應第三個元素
142             if("106".equals(encrypt) && pd[2]=='y'){
143                 datFileList.add(temp);
144             }
145             
146         } 
147     }
148 
149     //對zip和sql文件名按從小到大排序
150     Collections.sort(fileList, new Comparator<File>() {
151         @Override
152         public int compare(File o1, File o2) {
153             if (o1.isDirectory() && o2.isFile())
154                 return -1;
155             if (o1.isFile() && o2.isDirectory())
156                 return 1;
157             return o1.getName().compareTo(o2.getName());
158         }
159     });
160 
161     
162     
163     
164     //對比補丁包和sql文件,找出文件名(時間)大於傳來的名稱
165     if(fileList.size()>0){
166 
167         
168         for(int i=0;i<fileList.size();i++){
169             
170             String tempFileName = ((File) fileList.get(i)).getName();
171             
172             //System.out.println(tempFileName);
173             
174             String qzName = tempFileName.split("\\.")[0];//取前綴
175             String hzName = tempFileName.split("\\.")[1];//取后綴
176 
177             
178             
179             //if(tempName)
180             
181             if("zip".equals(hzName)){
182 
183                 //傳來的最后一次更新名有可能為空
184                 if(!"".equals(lastZipName)){
185             
186                     //大於傳來的文件名的就是客戶端還未更新的文件,將文件名記入list作為結果返回
187                     long lastUpdatepatchNameInt = Long.parseLong(lastZipName.split("\\.")[0]);
188                     long tempNameInt = Long.parseLong(qzName);
189                     if(tempNameInt>lastUpdatepatchNameInt){
190 
191                         result += ","+tempFileName;
192                     }
193                 }else{
194 
195                     result += ","+tempFileName;
196                 }
197             }else if("sql".equals(hzName)){
198 
199                 //傳來的最后一次更新名有可能為空
200                 if(!"".equals(lastSqlName)){
201             
202                     //大於傳來的文件名的就是客戶端還未更新的文件,將文件名記入list作為結果返回
203                     long lastUpdatepatchNameInt = Long.parseLong(lastSqlName.split("\\.")[0]);
204                     long tempNameInt = Long.parseLong(qzName);
205                     if(tempNameInt>lastUpdatepatchNameInt){
206 
207                         result += ","+tempFileName;
208                     }
209                 }else{
210 
211                     result += ","+tempFileName;
212                 }
213             }
214                 
215             
216             
217         }
218 
219         
220     }
221     
222     
223     //對dat文件進行分組排序,同套試卷的找出最新包,返回最新包名
224     if(datFileList.size()>0){
225 
226         
227         //創建map,對試卷更新包按試卷編碼分組存儲。
228         HashMap<String,ArrayList<File>> paperMap = new HashMap<String,ArrayList<File>>();
229         for(int i=0;i<datFileList.size();i++){
230             File datFile = (File)datFileList.get(i);//當前文件
231             String paperNumber = datFile.getName().split("_")[0];//取得試卷號(作為map的key)
232             ArrayList<File> tempList = paperMap.get(paperNumber);//從map中取該試卷的所有更新(一個ArrayList集合)
233             if(tempList==null){
234                 tempList = new ArrayList<File>();//如果為空,說明是第一次,則創建一個。
235             }
236             tempList.add(datFile);//將當前文件添加進list
237             paperMap.put(paperNumber,tempList);//將list放回map
238             
239         }
240         
241         //遍歷map,對各試卷分組的更新包進行按日期(明細)排序,取最后一個(最新包)名稱。
242         for (String key : paperMap.keySet()) {
243             ArrayList<File> tempList = paperMap.get(key);
244 
245             //文件名按從小到大排序
246             Collections.sort(tempList, new Comparator<File>() {
247                 @Override
248                 public int compare(File o1, File o2) {
249                     if (o1.isDirectory() && o2.isFile())
250                         return -1;
251                     if (o1.isFile() && o2.isDirectory())
252                         return 1;
253                     return o1.getName().compareTo(o2.getName());
254                 }
255             });
256 
257             
258             
259             //取排序后的最后一個的名稱
260             String lastPaperVersion = tempList.get(tempList.size()-1).getName();
261             //遍歷最后更新的試卷包名集合,檢查更新,如果傳來的試題包名中找不到lastPaperVersion,則說明試卷有新版本,則返回給客戶端。
262             if(paperNames!=null && paperNames.size()>0){
263                 boolean flag = false;
264                 for(String temp : paperNames){
265                     if(temp.equals(lastPaperVersion)){
266                         flag = true;
267                         break;
268                     }
269                 }
270                 
271                 if(flag==false){
272 
273                     result += ","+lastPaperVersion;            
274                 }
275             }else{
276                 result += ","+lastPaperVersion;//如果沒有傳來任何試卷信息,服務端卻又試卷更新包,那么說明有新的更新,客戶端初次安裝就是這種情況。
277             }
278             
279         }
280         
281     }
282     
283     
284     
285     
286     
287     if(!"".equals(result)){
288         result = result.substring(1);
289     }
290     
291 }
292 
293 
294 out.print(result);
295 
296 %>

 

客戶端:

請求檢查更新js(用到了jquery):

 1 var global_system_update_files=null;
 2 
 3 $(document).ready(function(){
 4 
 5 
 6 
 7 
 8     /**
 9      *  自動更新流程:
10      *  1.先得到本地最后一次更新的包名,請求遠程服務器,將包名上報,得到還未更新的文件列表
11      *  2.判斷返回的列表,如果有記錄,則彈出自動更新提示,管理員點立即更新后,循環遍歷文件列表,並請求后台執行更新.
12      *  3.更新完一個包就顯示一個進度.
13      */
14 
15     $.ajax({
16         url:"exam_admin/common/updater!checkUpdate",
17         type:"post",
18         async:true,
19         cache:false,
20         success:function(data,status){
21             var systemFiles = data.names;
22 
23             var tips = "";
24             tips += "<div id='updateTips' style='position:absolute;width:auto; display:inline-block !important; display:inline; top:4px;right:4px;line-height:20px;border:1px solid orangered;background-color:orange;text-align:center;padding:4px;'>";
25 
26 
27             if(systemFiles!=null && systemFiles!="" && systemFiles!="error"){
28                 systemFiles = systemFiles.split(",");
29                 global_system_update_files = systemFiles;
30 
31                 var zipCount = 0;
32                 var sqlCount = 0;
33                 var datCount = 0;
34                 for(var i=0;i<systemFiles.length;i++){
35                     var extendName = systemFiles[i].split(".")[1];
36                     if(extendName=="zip"){
37                         zipCount++;
38                     }
39                     else if(extendName=="sql"){
40                         sqlCount++;
41                     }
42                     else if(extendName=="dat"){
43                         datCount++;
44                     }
45 
46                 }
47 
48 
49 
50 
51                tips += "發現";
52                 if(zipCount>0){
53                     tips += zipCount+"個程序更新,";
54                 }
55                 if(sqlCount>0){
56                     tips += sqlCount+"個數據庫更新,";
57                 }
58                 if(datCount>0){
59                     tips += datCount+"個試題更新,";
60                 }
61 
62                 tips += "<a href='javascript:updateSystem()'>立即更新</a>";
63                 tips += "</div>";
64                 $(document.body).append(tips);
65 
66             }else if(systemFiles=="error"){
67 
68                 tips += "更新檢查失敗,請檢查網絡或加密狗是否正常.";
69                 tips += "</div>";
70                 $(document.body).append(tips);
71             }
72 
73 
74         }
75     });
76 
77 
78 
79     //
80 
81 
82 
83 });

 

后台請求檢查更新方法(java struts2 action):

 1     /***
 2      * 檢查更新
 3      * 請求服務器,獲得需要更新的補丁包或sql文件列表以及需要對比的最新試題包名.
 4      *
 5      * 補丁包和sql作為程序更新,與試題包的處理不同,本方法會獲得:
 6      * 還未更新的補丁包(包括zip和sql文件),所有最新試題包文件名
 7      * 還未更新的補丁包可以直接進行安裝,而試題更新包名需要與本地相關試卷最后一次更新進對比后確認是否執行更新.
 8      *
 9      * @return
10      */
11     public String checkUpdate(){
12 
13         names = "error";
14 
15         String dirPath = ServletActionContext.getServletContext().getRealPath("/");//得到目標目錄的絕對路徑
16         ///Users/jianyuchen/IdeaProjects/git_project/chanjetedu-exam-standard/exam-admin-mvc/target/exam-admin-mvc-prod-1.0-SNAPSHOT/
17         //路徑為webroot,取更新包名應該用../,也就是到exam/webapps下
18 
19         String lastUpdateNames = "";
20 
21         //定位文件,如果找不到,也說明是初次使用自動更新功能,則創建一個包目錄,用於存放下載的文件
22         File file = new File(dirPath+"../"+lastUpdateFileDirName+"/");
23         if(!file.exists()){
24             file.mkdir();
25             lastUpdateNames = "";
26         }else{
27 
28             File[] files=file.listFiles();
29 
30             String namesparameter = "";
31             if(files.length>0){
32                 for(File temp:files){
33                     namesparameter += ","+temp.getName();
34                 }
35                 lastUpdateNames = namesparameter.substring(1);
36             }else{
37                 lastUpdateNames = "";
38             }
39 
40         }
41 
42 
43         //根據最后一次更新文件名查詢遠程服務器還未更新的文件名串,返回的數據格式:201710201741.sql,201710201741.zip,201710201744.zip,201710201745.zip
44         try {
45 
46             Integer encrypt = 106;//moduleEncryption.getVersion();//將加密狗信息傳給服務器,服務器返回有權限更新的文件列表(不同的加密狗試題權限是不同的).
47             String url = server+"check_update.jsp?lastUpdateFileNames="+lastUpdateNames+"&encrypt="+encrypt;
48             HttpURLConnection huc = (HttpURLConnection) new URL(url).openConnection();
49             //創建輸入流讀取器對象
50             BufferedReader br = new BufferedReader(new InputStreamReader(huc.getInputStream(), "utf-8"));
51             huc.connect();
52             String data = "";
53             String line = null;
54             //循環按行讀取文本流
55             while ((line = br.readLine()) != null) {
56                 data += ("\r\n" + line);
57             }
58             data = data.trim();
59 
60             names = data;
61             huc.disconnect();
62         }catch (Exception e){
63             e.printStackTrace();
64         }
65 
66 
67         return "checkUpdate";
68     }

 

 

前端通過上面的方法得到需要更新的文件列表字符串,遍歷文件名請求后台執行更新的js

 1 /***
 2  * 執行更新函數
 3  * 函數將遍歷文件名列表,挨個挨個請求后台進行下載安裝.
 4  */
 5 function updateSystem(){
 6     if(global_system_update_files!=null && global_system_update_files.length>0){
 7 
 8         var index = 0;
 9 
10 
11 
12 
13         //創建一個匿名函數去執行請求
14         (function(){
15 
16             var name = global_system_update_files[index];
17 
18 
19 
20             var funarg = arguments;//得到匿名函數本身引用
21 
22             $("#updateTips").html("正在安裝第"+(index+1)+"個更新:"+name);
23 
24             //請求后台執行更新
25             $.ajax({
26                 url:"exam_admin/common/updater!executeUpdate?name="+name,
27                 type:"post",
28                 async:true,
29                 cache:false,
30                 success:function(data,status){
31 
32                     //所有的更新完后則不再遞歸執行.
33                     if(index==global_system_update_files.length-1){
34 
35                         $("#updateTips").css("border","1px solid #00aa00").css("background-color","#66ff66");
36                         $("#updateTips").html("更新完成,請重啟服務器后再使用!");
37                         return;
38                     }
39 
40                     index++;
41 
42                     //匿名函數自我遞歸調用,執行下一個更新
43                     setTimeout(function(){
44                         funarg.callee();
45                     },2000);
46 
47 
48 
49                 },
50                 error:function(){
51                     $("#updateTips").css("border","1px solid orangered").css("background-color","lightcoral");
52                     $("#updateTips").html("第"+(index+1)+"個更新:"+name+"安裝失敗.");
53 
54                 }
55             });
56 
57 
58 
59         })();
60 
61 
62 
63 
64 
65 
66 
67     }
68 
69 }

 

后台下載執行更新

  1     /***
  2      * 執行更新
  3      * 下載文件,判斷類型,執行解壓或調用sql腳本執行器或執行試題的更新
  4      * @return
  5      */
  6     public String executeUpdate(){
  7 
  8         System.out.println("正在安裝:"+name);
  9 
 10 
 11         String webapps = ServletActionContext.getServletContext().getRealPath("/")+"../";//得到目標目錄的絕對路徑
 12 
 13         String ext = name.substring(name.length()-3);
 14 
 15         //刪除上一次更新的文件
 16         File f=new File(webapps+""+lastUpdateFileDirName+"/");
 17         File[] fs=f.listFiles();
 18         for(File t:fs){
 19 
 20             //不同后綴的文件只會有一個,所以后綴相等就找到了上次更新的文件,然后刪掉
 21             if(!"dat".equals(ext)){
 22                 if(t.getName().split("\\.")[1].equals(ext)){
 23                     t.delete();
 24                 }
 25             }else{//由於試卷更新包dat文件組織和命名比較特殊,需要特殊處理
 26                 //試題更新包名稱格式:666_yyn_20171205.dat
 27                 //刪除試題更新包時,應對比試卷號,不能刪除掉其他試卷包.
 28 
 29                 String tempName1 = t.getName().split("_")[0];
 30                 String tempName2 = name.split("_")[0];
 31                 if(tempName1.equals(tempName2)){
 32                     t.delete();
 33                 }
 34 
 35             }
 36 
 37         }
 38 
 39 
 40         //1.根據文件名從遠程服務器下載文件
 41         String url = server+"files/"+name;
 42 
 43         String tempfilename = url.substring(url.lastIndexOf("/")+1,url.lastIndexOf("."));
 44         File download=new File(webapps+""+lastUpdateFileDirName+"/"+name);
 45         byte[] tempfile = null;
 46         try {
 47             URL u = new URL(url);
 48             InputStream is = u.openStream();
 49 
 50             //取得文件真實類型(不通過后綴和content-type)
 51             //BufferedInputStream bis = new BufferedInputStream(is);
 52             //String fileType = HttpURLConnection.guessContentTypeFromStream(bis);
 53             //System.out.println(fileType);
 54 
 55             FileOutputStream os = new FileOutputStream(download);
 56             byte[] buffer = new byte[1024];
 57             int length = -1;
 58             while((length = is.read(buffer))!=-1){
 59                 os.write(buffer,0,length);
 60             }
 61             is.close();
 62             os.flush();
 63             os.close();
 64         } catch (Exception e) {
 65             e.printStackTrace();
 66         }
 67 
 68         //2.判斷文件類型,如果是zip執行解壓覆蓋,如果是sql,調用sql腳本執行,如果是dat(試題)文件,則執行導入.
 69         if(("zip").equals(ext)){
 70             //程序更新
 71 
 72             System.out.println("正在解壓"+name);
 73 
 74             ZipFile zip=null;
 75             InputStream in=null;
 76             OutputStream out=null;
 77 
 78             try {
 79                 zip=new ZipFile(download);
 80                 Enumeration<?> entries=zip.entries();
 81                 while(entries.hasMoreElements()){
 82                     ZipEntry entry=(ZipEntry) entries.nextElement();
 83                     String zipEntryName=entry.getName();
 84                     in=zip.getInputStream(entry);
 85 
 86                     String outPath=(webapps+zipEntryName).replace("\\*", "/");
 87                     //判斷路徑是否存在,不存在則創建文件路徑
 88                     File file=new File(outPath.substring(0, outPath.lastIndexOf('/')));
 89                     if(!file.exists()){
 90                         file.mkdirs();
 91                     }
 92                     //判斷文件全路徑是否為文件夾,如果是上面已經創建,不需要解壓
 93                     if(new File(outPath).isDirectory()){
 94                         continue;
 95                     }
 96                     out=new FileOutputStream(outPath);
 97 
 98                     byte[] buf=new byte[4*1024];
 99                     int len;
100                     while((len=in.read(buf))>=0){
101                         out.write(buf, 0, len);
102                     }
103                     in.close();
104 
105                     //System.out.println("==================解壓完畢==================");
106                 }
107             } catch (Exception e) {
108                 //System.out.println("==================解壓失敗==================");
109                 //刪除執行失敗的更新包,以便下次再次更新.
110                 download.delete();
111                 e.printStackTrace();
112 
113             }finally{
114                 try {
115                     if(zip!=null){
116                         zip.close();
117                     }
118                     if(in!=null){
119                         in.close();
120                     }
121                     if(out!=null){
122                         out.close();
123                     }
124                 } catch (IOException e) {
125                     e.printStackTrace();
126                 }
127             }
128 
129         }else if(("sql").equals(ext)){
130             //sql腳本
131 
132 
133             System.out.println("正在執行"+name);
134 
135             StringBuffer sb = new StringBuffer();
136             try{
137                 BufferedReader br = new BufferedReader(new FileReader(download));//構造一個BufferedReader類來讀取文件
138                 String s = null;
139                 while((s = br.readLine())!=null){//使用readLine方法,一次讀一行
140                     sb.append(System.lineSeparator()+s);
141                 }
142                 br.close();
143                 updaterService.ddl_executeSqlScript(sb);
144             }catch(Exception e){
145                 //刪除執行失敗的更新包,以便下次再次更新.
146                 download.delete();
147                 e.printStackTrace();
148             }
149 
150         }else if(("dat").equals(ext)){
151             //試卷更新
152 
153             //調用試卷包處理方法
154             HashMap<String,Object> paperDetailMap;//創建題數據map
155             try {
156                 // 解密,雲平台導出題是用1進行加的密,解密時也用1解密,雲平台TblpaperAction的DEFAULT_PASS變量.
157                 byte[] buffer = Files.toByteArray(download);
158                 paperDetailMap = Encryptor.i("1").decrypt(HashMap.class, buffer);
159 
160                 if(paperDetailMap != null && !paperDetailMap.isEmpty()){
161                     paperDetailMap.put("orgcode",getCookieValue(Constant.SESSION_ADMIN_ORGCODE));
162                     paperDetailMap.put("adminUserid",getCookieValue(Constant.SESSION_ADMINUSERID));
163 
164                     /***
165                      * 獲取map對象中所有圖片的byte[],並讀出到相應路徑的文件中
166                      * 1、確定圖片路徑
167                      * 2、將圖片讀出到指定路徑下
168                      * 3、刪除map中的圖片信息
169                      */
170                     paperDetailMap = tblpaperService.exportPictureToLocalPath(getRequest(),paperDetailMap);
171                     //新添加的方法,2017.08.10 by wangweiaw  end
172 
173                     //將上傳的試卷信息保存到數據庫,此方法會判斷是否已存在,已存在會刪除,再新增.
174                     tblpaperService.ddl_savePaperDetailByMap(paperDetailMap);
175                 }else{
176 
177                 }
178 
179             }catch(Exception ex){
180                 //刪除執行失敗的更新包,以便下次再次更新.
181                 download.delete();
182                 ex.printStackTrace();
183             }
184 
185 
186         }
187 
188 
189         //執行完等待200毫秒,防止網速太快,執行太快,前端進度一閃而過,搞慢點,顯示個進度效果.
190         try {
191             Thread.currentThread().sleep(200);
192         } catch (InterruptedException e) {
193             e.printStackTrace();
194         }
195 
196         return "executeUpdate";
197     }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM