5.2 SpringBoot實現斷點續傳功能 > 我的程式猿之路:第四十二章
阿新 • • 發佈:2018-11-27
功能使用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