1. 程式人生 > >5.2 SpringBoot實現斷點續傳功能 > 我的程式猿之路:第四十二章

5.2 SpringBoot實現斷點續傳功能 > 我的程式猿之路:第四十二章

 

功能使用webuploader元件分片下載檔案 文件地址: http://fex.baidu.com/webuploader/document.html

 http://fex.baidu.com/webuploader/download.html中下載

用到的是:

              Uploader.swf

              webuploader.css

              webuploader.min.js

我的目錄

 

 

需要jar包:

        commons-fileupload-1.3.3.jar

        commons-io-2.5.jar

    匯入jar包方法:Shift+Ctrl+Alt+s

 

    按步驟匯入   jar包。。。

 

 

webUploader.java

  1 package com.example.demo3.upLoader;
2 3 import org.apache.commons.fileupload.FileItem; 4 import org.apache.commons.fileupload.FileUploadException; 5 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 6 import org.apache.commons.fileupload.servlet.ServletFileUpload; 7 import org.apache.commons.io.FileUtils; 8
import org.springframework.stereotype.Controller; 9 import org.springframework.web.bind.annotation.RequestMapping; 10 11 import javax.servlet.ServletException; 12 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletResponse; 14 import java.io.File; 15 import java.io.FileInputStream; 16 import java.io.FileOutputStream; 17 import java.io.IOException; 18 import java.util.HashMap; 19 import java.util.List; 20 21 @RequestMapping("uploader.do") 22 @Controller 23 public class WebUpLoaderController { 24 private static final long serialVersionUID = 1L; 25 @RequestMapping("loader") 26 public void douploader(HttpServletRequest request, HttpServletResponse response) 27 throws Exception { 28 String fileName = request.getParameter("fileName"); 29 String fileMd5 = request.getParameter("fileMd5"); 30 String chunk = request.getParameter("chunk"); 31 String chunkSize = request.getParameter("chunkSize"); 32 String guid = request.getParameter("guid"); 33 34 String path = ("C:/upload/uploads"); 35 File checkFile = new File(path+"/"+guid+"/"+chunk); 36 37 response.setContentType("text/html;charset=utf-8"); 38 39 //檢查檔案是否存在,且大小是否一致 40 if(checkFile.exists() && checkFile.length()==Integer.parseInt(chunkSize)){ 41 //上傳過 42 43 response.getWriter().write("{\"ifExist\":1}"); 44 45 }else{ 46 //沒有上傳過 47 48 response.getWriter().write("{\"ifExist\":0}"); 49 50 } 51 52 } 53 @RequestMapping("loader2") 54 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 55 56 String path = "C:\\upload\\uploads"; 57 String guid = request.getParameter("guid"); 58 String fileName = request.getParameter("fileName"); 59 /** 60 * 進行檔案合併 61 */ 62 File file = new File(path+"/"+guid); 63 new File("C:\\upload\\upload"+"/"+guid).mkdirs(); 64 /** 65 * 進行檔案合併 66 */ 67 File newFile = new File("C:\\upload\\upload"+"/"+guid+"/"+fileName); 68 FileOutputStream outputStream = new FileOutputStream(newFile, true);//檔案追加寫入 69 byte[] byt = new byte[10*1024*1024]; 70 int len; 71 FileInputStream temp = null;//分片檔案 72 File[] childs = new File(path+"/"+guid).listFiles(); 73 boolean boo = false; 74 if(childs!=null) { 75 for (int i = 0; i < childs.length; i++) { 76 temp = new FileInputStream(childs[i]); 77 while ((len = temp.read(byt)) != -1) { 78 //System.out.println(len); 79 outputStream.write(byt, 0, len); 80 } 81 temp.close(); 82 boo=true; 83 } 84 } 85 /** 86 * 當所有追加寫入都寫完 才可以關閉流 87 */ 88 outputStream.close(); 89 if(temp!=null){ 90 temp.close(); 91 } 92 if(boo) { 93 delFolder(path + "/" + guid); 94 } 95 96 } 97 @RequestMapping("loader1") 98 protected void doupload(HttpServletRequest request, HttpServletResponse response) 99 throws ServletException, IOException { 100 String path = "C:\\upload\\uploads"; 101 //DiskFileItemFactory factory = new DiskFileItemFactory(); 102 // 2、建立一個檔案上傳解析器 103 ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory()); 104 105 // 設定單個檔案的最大上傳值 106 upload.setFileSizeMax(15*1024*1024L); 107 // 設定整個request的最大值 108 upload.setSizeMax(15*1024*1024L); 109 // 解決上傳檔名的中文亂碼 110 upload.setHeaderEncoding("UTF-8"); 111 // fileUpload.setProgressListener(listener); 112 // 3、判斷提交上來的資料是否是上傳表單的資料 113 if (!ServletFileUpload.isMultipartContent(request)) { 114 return; 115 } 116 // 4、使用ServletFileUpload解析器解析上傳資料,解析結果返回的是一個List<FileItem>集合,每一個FileItem對應一個Form表單的輸入項 117 List<FileItem> list = null; 118 try { 119 120 list = upload.parseRequest(request); 121 // 獲得檔案: 122 // MultipartFile file = multipartRequest.getFile(" file "); 123 } catch (FileUploadException e) { 124 e.printStackTrace(); 125 } 126 127 HashMap<String, String> map = new HashMap<String, String>(); 128 129 for (FileItem item : list) { 130 if (item.isFormField()) { 131 String name = item.getFieldName(); 132 // 解決普通輸入項的資料的中文亂碼問題 133 String value = item.getString("UTF-8"); 134 135 map.put(name, value);// 放入map集合 136 } else { 137 /** 138 * 檔案上傳 139 */ 140 141 File fileParent = new File(path + "/" + map.get("guid"));//以guid建立臨時資料夾 142 if (!fileParent.exists()) { 143 fileParent.mkdir(); 144 } 145 146 147 String filename = item.getName(); 148 if (filename == null || filename.trim().equals("")) { 149 continue; 150 } 151 // 注意:不同的瀏覽器提交的檔名是不一樣的,有些瀏覽器提交上來的檔名是帶有路徑的,如: 152 // c:\a\b\1.txt,而有些只是單純的檔名,如:1.txt 153 // 處理獲取到的上傳檔案的檔名的路徑部分,只保留檔名部分 154 filename = filename.substring(filename.lastIndexOf("\\") + 1); 155 156 //建立檔案 157 File file; 158 if (map.get("chunks") != null) { 159 file = new File(fileParent, map.get("chunk")); 160 } else { 161 file = new File(fileParent, "0"); 162 } 163 164 //copy 165 FileUtils.copyInputStreamToFile(item.getInputStream(), file); 166 item.getInputStream().close(); 167 } 168 } 169 } 170 /*** 171 * 刪除資料夾 172 */ 173 public static void delFolder(String paths) { 174 try { 175 delAllFile(paths); // 刪除完裡面所有內容 176 String filePath = paths; 177 filePath = filePath.toString(); 178 File myFilePath = new File(filePath); 179 myFilePath.delete(); // 刪除空資料夾 180 } catch (Exception e) { 181 e.printStackTrace(); 182 } 183 } 184 /*** 185 * 刪除指定資料夾下所有檔案 186 * @return 187 */ 188 public static boolean delAllFile(String paths) { 189 boolean flag = false; 190 //判斷這個路徑名是否存在 191 File file = new File(paths); 192 if (!file.exists()) { 193 return flag; 194 } 195 //是否是一個目錄 196 if (!file.isDirectory()) { 197 return flag; 198 } 199 //返回由此抽象路徑名所表示的目錄中的檔案和目錄的名稱所組成字串陣列。 200 String[] tempList = file.list(); 201 File temp = null; 202 for (int i = 0; i < tempList.length; i++) { 203 //paths是否以File.separator結束 方便不同平臺下使用 204 if (paths.endsWith(File.separator)) { 205 //取一個 刪除一個 ... 206 temp = new File(file + tempList[i]); 207 } else { 208 //取一個 刪除一個 ... 209 temp = new File(file + File.separator + tempList[i]); 210 } 211 //是否是一個標準檔案。 212 if (temp.isFile()) { 213 //是的話刪除 214 temp.delete(); 215 flag = true; 216 } 217 //是否是一個目錄,如果是的話先刪除(delAllFile)目錄下的檔案,然後再刪除空資料夾 218 if (temp.isDirectory()) { 219 delAllFile(paths + "/" + tempList[i]);// 先刪除資料夾裡面的檔案 220 delFolder(paths + "/" + tempList[i]);// 再刪除空資料夾 221 flag = true; 222 } 223 } 224 return flag; 225 } 226 }

 

     webUploader.html

  1 <!DOCTYPE html>
  2 <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title>Upload Page</title>
  6     <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
  7     <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
  8 
  9     <script type="text/javascript" src="/upload/jquery-1.7.2.js"></script>
 10     <script type="text/javascript" src="/upload/webuploader.min.js"></script>
 11     <link href="/upload/webuploader.css" type="css/text" />
 12     <script type="text/javascript" src="/upload/jquery-2.0.0.min.js"></script>
 13     <script type="text/javascript" src="/upload/jquery-ui.js"></script>
 14     <link href="/upload/bootstrap.min.css" rel="stylesheet" media="screen">
 15     <script type="text/javascript" src="/upload/bootstrap.min.js"></script>
 16 
 17 </head>
 18 
 19 <body>
 20   <div style="margin: 22px 22px 22px 1px;">
 21       <div id="picker" class="form-control-focus">選擇檔案</div><br/>
 22       <p>允許上傳檔案型別:<b>.zip .dmp .rar .js .css .xml .7z .ico .pdf .ppt .pptx .xap .xpi .swf .apk .cdf .gif .tar .gz .sh .bmp .jpg</b><br />
 23       一次上傳檔案大小限制不小於:<b>50</b>MB</p>
 24 
 25       <table id="thelist" class="table table-bordered;uploader-list">
 26           <thead>
 27           <tr>
 28               <th>檔名</th>
 29               <th>檔案大小</th>
 30               <th>狀態</th>
 31               <th>操作</th>
 32           </tr>
 33           </thead>
 34       </table>
 35 
 36   </div>
 37   <button id="btnSync" type="button" class="btn btn-warning">開始同步</button>
 38 
 39 
 40 
 41 
 42 
 43   <script>
 44       var fileMd5;  //檔案唯一標識
 45 
 46       /******************下面的引數是自定義的*************************/
 47       var fileName;//檔名稱
 48       var oldJindu;//如果該檔案之前上傳過 已經上傳的進度是多少
 49       var count=0;//當前正在上傳的檔案在陣列中的下標,一次上傳多個檔案時使用
 50       var filesArr=new Array();//檔案陣列:每當有檔案被新增進佇列的時候 就push到陣列中
 51       var map={};//key儲存檔案id,value儲存該檔案上傳過的進度
 52       WebUploader.Uploader.register({
 53               "before-send-file":"beforeSendFile",//整個檔案上傳前
 54               "before-send":"beforeSend",  //每個分片上傳前
 55               "after-send-file":"afterSendFile",  //分片上傳完畢
 56           },
 57           {
 58               //時間點1:所有分塊進行上傳之前呼叫此函式
 59               beforeSendFile:function(file){
 60               //    alert('----');
 61                   var deferred = WebUploader.Deferred();
 62                   //1、計算檔案的唯一標記fileMd5,用於斷點續傳  如果.md5File(file)方法裡只寫一個file引數則計算MD5值會很慢 所以加了後面的引數:10*1024*1024
 63                   (new WebUploader.Uploader()).md5File(file,0,10*1024*1024).progress(function(percentage){
 64                       $('.'+file.id ).find('p.state').text('正在讀取檔案資訊...');
 65                   })
 66                       .then(function(val){
 67                           $('.'+file.id ).find("p.state").text("成功獲取檔案資訊...");
 68                           fileMd5=val;
 69                           uploader.options.formData.guid = fileMd5;
 70                           console.log("fileMd5:"+fileMd5);
 71                           //獲取檔案資訊後進入下一步
 72                           deferred.resolve();
 73                       });
 74 
 75                   fileName=file.name; //為自定義引數檔名賦值
 76                   return deferred.promise();
 77               },
 78               //時間點2:如果有分塊上傳,則每個分塊上傳之前呼叫此函式
 79               beforeSend:function(block){
 80              //     alert('-******-');
 81                   var deferred = WebUploader.Deferred();
 82                   $.ajax({
 83                       type:"POST",
 84                       url:"/uploader.do/loader",  //ajax驗證每一個分片
 85                       data:{
 86                           fileName : fileName,
 87                           fileMd5:fileMd5,  //檔案唯一標記
 88                           chunk:block.chunk,  //當前分塊下標
 89                           chunkSize:block.end-block.start,//當前分塊大小
 90                           guid: uploader.options.formData.guid
 91                       },
 92                       cache: false,
 93                       async: false,  // 與js同步
 94                       timeout: 1000,
 95                       dataType:"json",
 96                       success:function(response){
 97                           console.log(block.chunk+"--"+response.ifExist);
 98                           if(response.ifExist){
 99                               //分塊存在,跳過
100                               deferred.reject();
101                           }else{
102                             //  alert("ss11")
103                               //分塊不存在或不完整,重新發送該分塊內容
104                               deferred.resolve();
105                           }
106                       }
107                   });
108 
109                   this.owner.options.formData.fileMd5 = fileMd5;
110                   deferred.resolve();
111                   return deferred.promise();
112               },
113               //時間點3:所有分塊上傳成功後呼叫此函式
114               afterSendFile:function(){
115                 //  alert('-***2222**-');
116                   //如果分塊上傳成功,則通知後臺合併分塊
117                   $.ajax({
118                       type:"POST",
119                       url:"${ctx}/mergeOrCheckChunks.do?param=mergeChunks",  //ajax將所有片段合併成整體
120                       data:{
121                           fileName : fileName,
122                           fileMd5:fileMd5,
123                       },
124                       success:function(data){
125                           count++; //每上傳完成一個檔案 count+1
126                           ;                      if(count<=filesArr.length-1){
127                               uploader.upload(filesArr[count].id);//上傳檔案列表中的下一個檔案
128                           }
129                           //合併成功之後的操作
130                       }
131                   })
132               }
133           });
134       var uploader = WebUploader.create({
135 
136           // swf檔案路徑
137           swf : 'upload/Uploader.swf',
138           // 檔案接收服務端。
139           server : '/uploader.do/loader1',
140           // 選擇檔案的按鈕。可選。
141           // 內部根據當前執行是建立,可能是input元素,也可能是flash.
142           pick : '#picker',
143           chunked: true,  //分片處理
144           chunkSize: 10 * 1024 * 1024, //每片5M
145           threads:3,//上傳併發數。允許同時最大上傳程序數。
146           // 不壓縮image, 預設如果是jpeg,檔案上傳前會壓縮一把再上傳!
147           resize : false
148       });
149 
150       function removeSection(e) {
151        //   alert("當前第幾行:"+e.parentElement.parentElement.rowIndex)
152           //alert("del:":+e.parentElement.parentElement.removeNode(true))
153           //var a = e.parentElement.parentElement.rowIndex;
154           //var table=document.getElementById("thelist");
155           //var objt = $(obj);.remove();
156           //    uploader.removeFile( file );
157           var tr=e.parentNode.parentNode;
158           tr.parentNode.removeChild(tr);
159           //e.parentNode.removeChild(e.parentElement.parentElement.rowIndex);
160           //table.deleteRow(e.parentElement.parentElement.rowIndex);
161           //alert(table)
162           //var len=table.rows.length;
163           //alert(len)
164           //      alert("ss"+e.id+"==="+e.name)
165           //    $(e).parents(e.id).remove();
166           //    $('[data-spy="scroll"]').each(function () {
167           //        var $spy = $(this).scrollspy('refresh')
168           //        });
169       }
170       // 當有檔案被新增進佇列的時候
171       uploader.on('fileQueued', function(file) {
172           //    alert(file.size)
173           var fileName = file.name
174           if (fileName.indexOf('#') >= 0) {
175               alert('檔名中不能有井號');
176               return false;
177           }
178           if (fileName.indexOf('+') >= 0) {
179               alert('檔名中不能有加號');
180               return false;
181           }
182           if (fileName.indexOf(' ') >= 0) {
183               alert('檔名中不能有空格');
184               return false;
185           }
186           var reg = /(\.zip|\.dmp|\.rar|\.js|\.css|\.xml|\.7z|\.ico|\.pdf|\.ppt|\.pptx|\.xap|\.xpi|\.swf|\.apk|\.cdf|\.gif|\.tar|\.gz|\.sh|\.bmp|\.jpg)$/ig;
187           if (!reg.test(fileName)) {
188               var supportType = reg.toString().replace(/\/|\(|\)|\\|/ig, '');
189               supportType = supportType.substring(0, supportType.length - 3);
190               supportType = supportType.replace(/\|/g, ' ');
191               alert('不支援該上傳檔案型別!\n支援的檔案型別:' + supportType);
192            //   return false;
193           }
194           if (file.size < 50*1024*1024)
195           {
196               alert('上傳檔案大小不能小於' + 50 + 'MB');
197             //  return false;
198           }
199         //  alert(123);
200           $("#thelist").append(
201               '<tr class="'+ file.id +'">'
202               +'<td ><h4 class="info">' + file.name + '</h4></td>'
203               +'<td><font face="宋體" size=3 color="red">'+ file.size +' </font>'+'位元組'+'</td>'
204               +'<td id="' + file.id + '"><p class="state">等待上傳...</p></td>'//onclick="removeSection(this);"
205               +'<td><input type="button" class="remove-this" name="sss" id="'+ file.id +'"  value="刪除" class="btn btn-default"/></td>'
206               +'</tr>');
207       });
208 
209       uploader.on('uploadProgress', function(file,percentage) {
210 
211           var $li = $('#' + file.id),
212               $percent = $li.find('.progress .progress-bar');
213 
214           // 避免重複建立
215           if (!$percent.length) {
216               $percent = $('<div id="' + file.id + '" class="progress progress-striped active">' +
217                   '<div class="progress-bar" style="height:20px; role="progressbar" style="width: 0%">' +
218                   '</div>' +
219                   '</div>').appendTo($li).find('.progress-bar');
220           }
221 
222           $('.'+file.id ).find('p.state').text('上傳中');
223           $percent.css('width', percentage * 100 + '%');
224       });
225 
226       uploader.on('uploadSuccess', function(file) {
227           $('.' + file.id).find('p.state').text('已上傳');
228           $.post("/uploader.do/loader2", { "guid": uploader.options.formData.guid,fileName:file.name},
229               function(data){
230               }, "json");
231       });
232 
233       uploader.on('uploadError', function(file) {
234           $('.' + file.id).find('p.state').text('上傳出錯');
235       });
236 
237       uploader.on('uploadComplete', function(file) {
238           $('.' + file.id).find('.progress').fadeOut();
239       });
240 
241       uploader.on( 'beforeFileQueued', function( file ) {
242 
243        //   alert(file.size);
244 
245       });
246 
247       $("#btnSync").on('click', function() {
248           if ($(this).hasClass('disabled')) {
249               return false;
250           }
251           console.log("get fileMd5:"+fileMd5);
252 
253           uploader.upload();
254 
255       });
256   </script>
257 </body>
258 </html>

 

application.propertise

#檔案分片上傳關閉multipart,不然的話request被預編譯,檔案解析器解析請求為空
spring.servlet.multipart.enabled=false

 

 

 

 

遇到的問題:

list = upload.parseRequest(request);(74行) list為空
我用Meclipse可以,idea就不行了,很納悶。
網上找了很多,知道request被預編譯,但是說法大多數struts配置影響的還有一些其他方法,
看來下,臥槽,那麼複雜,我這麼lazy,算了,再找找其他的。
最後在一片博文找到了方法,在application.propertise中
關閉multipart,問題解決
spring.servlet.multipart.enabled=false