Baidu WebUploader 前端文件上傳組件的使用


簡介

WebUploader是由Baidu WebFE(FEX)團隊開發的一個簡單的以HTML5為主,FLASH為輔的現代文件上傳組件。在現代的瀏覽器里面能充分發揮HTML5的優勢,同時又不摒棄主流IE瀏覽器,沿用原來的FLASH運行時,兼容IE6+,iOS 6+, android 4+。兩套運行時,同樣的調用方式,可供用戶任意選用。 采用大文件分片並發上傳,極大的提高了文件上傳效率。

特點

分片、並發

分片與並發結合,將一個大文件分割成多塊,並發上傳,極大地提高大文件的上傳速度。當網絡問題導致傳輸錯誤時,只需要重傳出錯分片,而不是整個文件。另外分片傳輸能夠更加實時的跟蹤上傳進度。

預覽、壓縮

支持常用圖片格式jpg,jpeg,gif,bmp,png預覽與壓縮,節省網絡數據傳輸。解析jpeg中的meta信息,對於各種orientation做了正確的處理,同時壓縮后上傳保留圖片的所有原始meta數據。

多途徑添加文件

支持文件多選,類型過濾,拖拽(文件&文件夾),圖片粘貼功能。粘貼功能主要體現在當有圖片數據在剪切板中時(截屏工具如QQ(Ctrl + ALT + A), 網頁中右擊圖片點擊復制),Ctrl + V便可添加此圖片文件。

HTML5 & FLASH

兼容主流瀏覽器,接口一致,實現了兩套運行時支持,用戶無需關心內部用了什么內核。同時Flash部分沒有做任何UI相關的工作,方便不關心Flash的用戶擴展和自定義業務需求。

MD5秒傳

當文件體積大、量比較多時,支持上傳前做文件md5值驗證,一致則可直接跳過。如果服務端與前端統一修改算法,取段md5,可大大提升驗證性能,耗時在20ms左右。

易擴展、可拆分

采用可拆分機制,將各個功能獨立成了小組件,可自由搭配。采用AMD規范組織代碼,清晰明了,方便高級玩家擴展。

下載

下載地址:https://github.com/fex-team/webuploader/releases

可以選擇最新版進行下載。下載的壓縮包中有如下文件。

├── Uploader.swf                      // SWF文件,當使用Flash運行時需要引入。

├── webuploader.js                    // 完全版本。
├── webuploader.min.js                // min版本

├── webuploader.nolog.js              // 完全版本不帶日志功能。
├── webuploader.nolog.min.js          // min版本

├── webuploader.flashonly.js          // 只有Flash實現的版本。
├── webuploader.flashonly.min.js      // min版本

├── webuploader.html5only.js          // 只有Html5實現的版本。
├── webuploader.html5only.min.js      // min版本

├── webuploader.withoutimage.js       // 去除圖片處理的版本,包括HTML5和FLASH.
└── webuploader.withoutimage.min.js   // min版本

示例

具體文檔可以直接參考官方網站的說明,這個使用說明還是很詳細的,這里就不重復搬運了。

官網地址:http://fex.baidu.com/webuploader/doc/index.html

下面看一下我們項目中的實際應用。

前端代碼

先引入相關js文件:

<script type="text/javascript" src="js/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="js/webuploader.nolog.js"></script>

頁面引入相關控件:

<div id="uploadBtn" class="btn btn-upload">上傳文件</div>
<div id="list" class="upload-box clearfix"></div>

前端js代碼:

  1 //驗證數組方法
  2 Array.prototype.contains = function (obj) {  
  3     var i = this.length;  
  4     while (i--) {  
  5         if (this[i] === obj) {  
  6             return true;  
  7         }  
  8     }  
  9     return false;  
 10 }
 11 
 12 var arr_md5 = [];//加載頁面時,將已上傳成功的分片數組化
 13 var chunks = 0;//分片總數量,用來上傳成功以后校驗分片文件總數
 14 var repart_chunks = 0;
 15 var upload_success = false;
 16 var times = 0;
 17 var interval = 0;
 18 
 19 //注冊組件
 20 WebUploader.Uploader.register({
 21     'before-send': 'preupload'
 22 }, {
 23     preupload: function( block ) {
 24         var me = this,
 25             owner = this.owner,
 26             deferred = WebUploader.Deferred();
 27             chunks = block.chunks;
 28             owner.md5File(block.blob)
 29             //及時顯示進度
 30             .progress(function(percentage) {
 31                 console.log('Percentage:', percentage);
 32             })
 33             //如果讀取出錯了,則通過reject告訴webuploader文件上傳出錯。
 34             .fail(function() {
 35                 deferred.reject();
 36                 console.log("==1==");
 37             })
 38             //md5值計算完成
 39             .then(function( md5 ) {
 40                 if(arr_md5.contains(md5)){//數組中包含+(block.chunk+1)
 41                     deferred.reject();
 42                     console.log("分片已上傳"+block.file.id);
 43                     $("#speed_"+block.file.id).text("正在斷點續傳中...");
 44                     if(block.end == block.total){
 45                         $("#speed_"+block.file.id).text("上傳完畢");
 46                         $("#pro_"+block.file.id).css( 'width', '100%' );
 47                     }else{
 48                         $("#pro_"+block.file.id).css( 'width',(block.end/block.total) * 100 + '%' );
 49                     }
 50                 }else{
 51                     deferred.resolve();
 52                     console.log("開始上傳分片:"+md5);
 53                 }
 54             });
 55         return deferred.promise();
 56     }
 57 });
 58 
 59 //初始化WebUploader
 60 var uploader = WebUploader.create({
 61     //swf文件路徑
 62     //swf: 'http://www.ssss.com.cn/js/webuploader/Uploader.swf',
 63     //文件接收服務端。
 64     server: '/upload/UploadPhotoSlt?methodName=fileupload&tokenid='+$("#tokenid").val(),
 65     //選擇文件的按鈕,可選。內部根據當前運行是創建,可能是input元素,也可能是flash.
 66     pick: '#uploadBtn',
 67     //不壓縮image, 默認如果是jpeg,文件上傳前會壓縮一把再上傳!
 68     resize: false,
 69     auto:true,
 70     //是否分片
 71     chunked :true,
 72     //分片大小
 73     chunkSize :1024*1024*2,
 74     chunkRetry :3,
 75     threads :3,//最大並發
 76     fileNumLimit :1,
 77     fileSizeLimit :1024*1024*1024*1024,
 78     fileSingleSizeLimit: 1024*1024*1024,
 79     duplicate :true,
 80     accept: {
 81         title: 'file',
 82         extensions: 'jpg,png,ai,zip,rar,psd,pdf,cdr,psd,tif',
 83         mimeTypes: '*/*'
 84     }
 85 });
 86 
 87 //當文件被加入隊列之前觸發,此事件的handler返回值為false,則此文件不會被添加進入隊列。
 88 uploader.on('beforeFileQueued',function(file){
 89     if(",jpg,png,ai,zip,rar,psd,pdf,cdr,psd,tif,".indexOf(","+file.ext+",")<0){
 90         alert("不支持的文件格式");
 91     }
 92 });
 93 
 94 //當有文件添加進來的時候 
 95 uploader.on( 'fileQueued', function( file ) {
 96     times = 1;
 97     var src = "http://www.wodexiangce.cn/images/type/JPG.jpg";
 98     if(file.ext == 'jpg'){
 99         src = "http://www.wodexiangce.cn/images/type/JPG.jpg";
100     }else if(file.ext == 'png'){
101         src = "http://www.wodexiangce.cn/images/type/PNG.jpg";
102     }else if(file.ext == 'ai'){
103         src = "http://www.wodexiangce.cn/images/type/AI.jpg";
104     }else if(file.ext == 'zip'){
105         src = "http://www.wodexiangce.cn/images/type/ZIP.jpg";
106     }else if(file.ext == 'rar'){
107         src = "http://www.wodexiangce.cn/images/type/RAR.jpg";
108     }else if(file.ext == 'psd'){
109         src = "http://www.wodexiangce.cn/images/type/PSD.jpg";
110     }else if(file.ext == 'pdf'){
111         src = "http://www.wodexiangce.cn/images/type/PDF.jpg";
112     }else if(file.ext == 'cdr'){
113         src = "http://www.wodexiangce.cn/images/type/CDR.jpg";
114     }else if(file.ext == 'tif'){
115         src = "http://www.wodexiangce.cn/images/type/TIF.jpg";
116     }
117     upload_success = false;
118     $("#list").html( 
119              '<div class="clearfix"><img src='+src+' width="50px" class="icon-file" ></img>'+
120                 '<div class="fl" style="margin-left: 70px;">'+
121                 '<p class="speed font-12" id="speed_'+file.id+'">校驗文件...</p>'+
122                 '<div class="progress">'+
123                     '<span id="pro_'+file.id+'" class="progressing"></span>'+
124                 '</div>'+
125                 '<span class="file-size">'+(file.size/1024/1024).toFixed(2)+'MB</span>'+
126                 '<a href="javascript:void(0);" id="stopOretry_'+file.id+'" onclick="stop(\''+file.id+'\');" class="a-pause">暫停</a>'+
127                 '</div></div><span class="file-name">'+file.name+'</span><br/>' );
128         //文件開始上傳后開始計時,計算實時上傳速度
129        interval = setInterval(function(){times++;},1000);
130         
131 });
132 
133 //上傳過程中觸發,攜帶上傳進度。文件上傳過程中創建進度條實時顯示。  
134 uploader.on( 'uploadProgress', function( file, percentage ) {  
135     var status_pre = file.size*percentage-arr_md5.length*2*1024*1024;
136     if(status_pre<=0){
137         return;
138     }
139     $("#pro_"+file.id).css( 'width', percentage * 100 + '%' );
140     var speed = ((status_pre/1024)/times).toFixed(0);
141     $("#speed_"+file.id).text(speed +"KB/S");
142     if(percentage == 1){
143         $("#speed_"+file.id).text("上傳完畢");
144     }
145 }); 
146 
147 //文件上傳成功時觸發
148 uploader.on( 'uploadSuccess', function( file,response) {
149     console.log("成功上傳"+file.name+"  res="+response);
150     $.ajax({
151         type:'get',
152         url:'/upload/UploadPhotoSlt',
153         dataType: 'json',
154         data: {
155             methodName:'composeFile',
156             name:file.name,
157             chunks:chunks,
158             tokenid:$("#tokenid").val()
159         },
160         async:false,
161         success: function(data) {
162             console.log("==compose=="+data.status);
163             if(data.status == "success"){
164                 upload_success = true;
165                 $("#url").val(data.url);
166                 console.log(data.url);
167             }else{
168                 upload_success = false;
169                 alert(data.errstr);
170             }
171         }
172     });
173 });
174 
175 //文件上傳異常失敗觸發
176 uploader.on( 'uploadError', function( file,reason ) {
177     console.log("失敗"+reason );
178 });
179 
180 //某個文件開始上傳前觸發,一個文件只會觸發一次。
181 uploader.on( 'uploadStart', function(file) {
182     console.info("file="+file.name);
183     //分片文件上傳之前
184     $.ajax({
185         type:'get',
186         url:'/upload/UploadPhotoSlt',
187         dataType: 'json',
188         data: {
189             methodName:'md5Validation',
190             name:file.name,
191             tokenid:$("#tokenid").val()
192         },
193         async:false,
194         success: function(data) {
195             if(data.md5_arr != ""){
196                 arr_md5 = data.md5_arr.split(",")
197             }else{
198                 arr_md5 = new Array();
199             }
200         }
201     });
202 });
203 
204 //當所有文件上傳結束時觸發。
205 uploader.on( 'uploadFinished', function() {
206    
207 });
208 
209 function stop(id){
210     uploader.stop(true);
211     $("#stopOretry_"+id).attr("onclick","retry('"+id+"')");
212     $("#stopOretry_"+id).text("恢復");
213      clearInterval(interval);
214 }
215 function retry(id){
216     uploader.retry();
217     $("#stopOretry_"+id).attr("onclick","stop('"+id+"')");
218     $("#stopOretry_"+id).text("暫停");
219     interval = setInterval(function(){times++;},1000);
220 }

后端代碼

后台Java代碼,使用到commons-fileupload-1.1.1.jar

可以參考:Apache Commons FileUpload 實現文件上傳

servlet:HttpServletBasic 

 1 package com.wodexiangce.web.servlet;
 2 
 3 import java.lang.reflect.InvocationTargetException;
 4 import java.lang.reflect.Method;
 5 
 6 import javax.servlet.ServletContext;
 7 import javax.servlet.ServletException;
 8 import javax.servlet.http.HttpServlet;
 9 import javax.servlet.http.HttpServletRequest;
10 import javax.servlet.http.HttpServletResponse;
11 
12 import org.springframework.web.context.WebApplicationContext;
13 import org.springframework.web.context.support.WebApplicationContextUtils;
14 
15 public class HttpServletBasic extends HttpServlet{
16     private static final long serialVersionUID = 1L;
17      
18     protected WebApplicationContext wac = null;
19 
20     public void init() throws ServletException {
21         super.init();
22         ServletContext sc = this.getServletContext();
23         wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
24     }
25     
26     /**
27      * 方法轉發器,通過方法名調用相應的方法 。
28      * 可以看出,不能分出方法同名而參數不同的方法 。
29      */ 
30     protected void service(HttpServletRequest request,HttpServletResponse response){
31  
32         String methodName = request.getParameter("methodName") ;
33          
34         //當前類所有的方法名稱
35         Method[] methods  = this.getClass().getDeclaredMethods() ; 
36         for(Method m:methods){
37             if(m.getName().equals(methodName)){
38                 try {
39                     m.invoke(this, new Object[]{request,response}) ;
40                 } catch (IllegalArgumentException e) {
41                     e.printStackTrace();
42                 } catch (IllegalAccessException e) {
43                     e.printStackTrace();
44                 } catch (InvocationTargetException e) {
45                     e.printStackTrace();
46                 } 
47                 break ; 
48             } 
49         }
50     }
51     
52     public void destroy() {
53         wac = null;
54     }
55     
56 } 

servlet:UploadPhotoSlt

  1 package com.wodexiangce.web.servlet;
  2 
  3 import java.io.File;
  4 import java.io.IOException;
  5 import java.util.Date;
  6 import java.util.HashMap;
  7 import java.util.List;
  8 import java.util.Map;
  9 
 10 import javax.servlet.http.HttpServletRequest;
 11 import javax.servlet.http.HttpServletResponse;
 12 
 13 import net.sf.json.JSONObject;
 14 
 15 import org.apache.commons.fileupload.FileItem;
 16 import org.apache.commons.fileupload.FileItemFactory;
 17 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
 18 import org.apache.commons.fileupload.servlet.ServletFileUpload;
 19 import org.apache.commons.lang.math.NumberUtils;
 20 
 21 import com.wodexiangce.persistence.model.WdxcQywFiles;
 22 import com.wodexiangce.services.QywFileService;
 23 import com.wodexiangce.util.FileUtils;
 24 
 25  
 26 public class UploadPhotoSlt extends HttpServletBasic{
 27     
 28     private static final long serialVersionUID = 1L;
 29 
 30     /**
 31      * 文件上傳
 32      * @param request
 33      * @param response
 34      * @throws IOException
 35      */
 36     @SuppressWarnings({ "unchecked", "deprecation" })
 37     public void fileupload(HttpServletRequest request, HttpServletResponse response)throws IOException{
 38         try {
 39             System.out.println("=================fileupload===================");
 40             response.addHeader("Access-Control-Allow-Origin", "*");
 41             String useridStr = request.getParameter("tokenid");
 42             if(useridStr==null||"".equals(useridStr)||useridStr.length()<3){
 43                 response.getWriter().write(JSONObject.fromObject("{\"status\":\"error\",'errstr':'token校驗錯誤'}").toString());
 44                 return;
 45             }
 46             long userid = Long.parseLong(useridStr);
 47             boolean isMultipart = ServletFileUpload.isMultipartContent(request);
 48             if (isMultipart) {
 49                 FileItemFactory factory = new DiskFileItemFactory();
 50                 ServletFileUpload upload = new ServletFileUpload(factory);
 51  
 52                 // 得到所有的表單域,它們目前都被當作FileItem
 53                 List<FileItem> fileItems = upload.parseRequest(request);
 54  
 55                 String id = "";
 56                 String fileName = "";
 57                 // 如果大於1說明是分片處理
 58                 int chunks = 1;
 59                 int chunk = 0;
 60                 FileItem tempFileItem = null;
 61                 for (FileItem fileItem : fileItems) {
 62                     if (fileItem.getFieldName().equals("id")) {
 63                         id = fileItem.getString();
 64                     } else if (fileItem.getFieldName().equals("name")) {
 65                         fileName = new String(fileItem.getString().getBytes("ISO-8859-1"), "UTF-8");
 66                     } else if (fileItem.getFieldName().equals("chunks")) {
 67                         chunks = NumberUtils.toInt(fileItem.getString());
 68                     } else if (fileItem.getFieldName().equals("chunk")) {
 69                         chunk = NumberUtils.toInt(fileItem.getString());
 70                     } else if (fileItem.getFieldName().equals("file")) {
 71                         tempFileItem = fileItem;
 72                     }
 73                 }
 74                 System.out.println("id="+id+"  filename="+fileName+"  chunks="+chunks+" chunk="+chunk+"  size="+tempFileItem.getSize());
 75                 //臨時目錄用來存放所有分片文件
 76                 String tempFileDir = getTempFilePath()+ File.separator + userid;
 77                 File parentFileDir = new File(tempFileDir);
 78                 if (!parentFileDir.exists()) {
 79                     parentFileDir.mkdirs();
 80                 }
 81                 //分片處理時,前台會多次調用上傳接口,每次都會上傳文件的一部分到后台
 82                 File tempPartFile = new File(parentFileDir, fileName + "_" + chunk+ ".part");
 83                 
 84                 String MD5 = FileUtils.copyInputStreamToFile(tempFileItem.getInputStream(),tempPartFile);
 85                 int count = 0;
 86                 while(MD5==null&&count<3){
 87                     MD5 = FileUtils.copyInputStreamToFile(tempFileItem.getInputStream(),tempPartFile);
 88                     count++;
 89                 }
 90                 if(MD5==null){
 91                     throw new  Exception("分片文件:"+chunk+"上傳失敗");
 92                 }
 93                 //分片文件上傳成功以后,重命名分片文件,規則:原名之前拼接MD5碼
 94                 tempPartFile.renameTo(new File(parentFileDir, fileName + "_" + chunk+"_"+MD5+ ".part"));
 95                 System.out.println("MD5="+MD5);
 96                 response.getWriter().write(JSONObject.fromObject("{\"md5\":\""+MD5+"\"}").toString());
 97             }
 98         } catch (Exception e) {
 99             e.printStackTrace();
100         }
101     }
102     
103     /**
104      * 重復驗證
105      * @param request
106      * @param response
107      * @throws IOException
108      */
109     public void md5Validation(HttpServletRequest request, HttpServletResponse response)throws IOException{
110         try {
111             System.out.println("=================md5Validation===================");
112             response.addHeader("Access-Control-Allow-Origin", "*");
113             response.setCharacterEncoding("utf-8");
114             String useridStr = request.getParameter("tokenid");
115             if(useridStr==null||"".equals(useridStr)||useridStr.length()<3){
116                 response.getWriter().write(JSONObject.fromObject("{\"status\":\"error\",'errstr':'token校驗錯誤'}").toString());
117                 return;
118             }
119             long userid = Long.parseLong(useridStr);
120             String tempFileDir = getTempFilePath()+ File.separator + userid;
121             File parentFileDir = new File(tempFileDir);
122             if (!parentFileDir.exists()) {
123                 response.getWriter().write(JSONObject.fromObject("{\"md5_arr\":\"\"}").toString());
124                 return;
125             }
126             String fileName = request.getParameter("name");
127             fileName = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
128             System.out.println("fileName="+fileName);
129             
130             StringBuffer sb = new StringBuffer();
131             for(File file:parentFileDir.listFiles()){
132                 String name = file.getName();
133                 if(name.endsWith(".part") && name.startsWith(fileName)){
134                     //此文件有上傳記錄,解析MD5
135                     name = name.substring(name.lastIndexOf("_")+1,name.lastIndexOf(".part"));
136                     if(name.length()==32){
137                         if(sb.length()>0){
138                             sb.append(",");
139                         }
140                         sb.append(name);
141                     }
142                 }
143             }
144             response.getWriter().write(JSONObject.fromObject("{\"md5_arr\":\""+sb.toString()+"\"}").toString());
145         } catch (Exception e) {
146             e.printStackTrace();
147         }
148     }
149     
150     /**
151      * 文件所有分片上傳完畢之后,保存文件數據到數據庫
152      * @param request
153      * @param response
154      * @throws IOException
155      */
156     public void composeFile(HttpServletRequest request, HttpServletResponse response)throws IOException{
157         try {
158             System.out.println("=================composeFile===================");
159             response.addHeader("Access-Control-Allow-Origin", "*");
160             response.setCharacterEncoding("utf-8");
161             String useridStr = request.getParameter("tokenid");
162             if(useridStr==null||"".equals(useridStr)||useridStr.length()<3){
163                 response.getWriter().write(JSONObject.fromObject("{\"status\":\"error\",'errstr':'token校驗錯誤'}").toString());
164                 return;
165             }
166             long userid = Long.parseLong(useridStr);
167             String tempFileDir = getTempFilePath()+ File.separator + userid;
168             File parentFileDir = new File(tempFileDir);
169             if (!parentFileDir.exists()) {
170                 response.getWriter().write(JSONObject.fromObject("{\"status\":\"error\",'errstr':'目錄不存在'}").toString());
171                 return;
172             }
173             String fileName = request.getParameter("name");
174             fileName = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
175             String chunks = request.getParameter("chunks");
176             System.out.println("fileName="+fileName);
177             Map<String,File> chunkFileMap = new HashMap<String, File>();
178             
179             String md5 = null;
180             for(File file:parentFileDir.listFiles()){
181                 String name = file.getName();
182                 if(name.endsWith(".part") && name.startsWith(fileName)){
183                     md5 = name.substring(name.lastIndexOf("_")+1,name.lastIndexOf(".part"));
184                     System.out.println("md5="+md5);
185                     if(md5.length()==32){//完整的分片文件
186                         String index = name.replace(fileName, "").replace("_"+md5+".part", "").replace("_", "");
187                         chunkFileMap.put(index, file);
188                     }
189                 }
190             }
191             //分片總數和分片文件數一致,則證明分片文件已完整上傳,可以持久化數據
192             if(chunkFileMap.size() == Integer.parseInt(chunks.trim())){
193                 System.out.println("===========開始合並文件分片==========");
194                 WdxcQywFiles QywFile = new WdxcQywFiles();
195                 QywFile.setFilename(fileName);
196                 QywFile.setCreatetime(new Date());
197                 QywFile.setFilepath("/site/photos/file/"+userid+"/"+fileName);
198                 QywFile.setFiledownurl("http://www.sssss.cn/file/"+userid+"/"+fileName);
199                 
200                 
201                 //AI、PDF、EPS、CDR、PSD、JPG、TIFF
202                 if(fileName.toLowerCase().endsWith(".tif")){
203                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/TIF.jpg");
204                 }else if(fileName.toLowerCase().endsWith(".jpg")){
205                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/JPG.jpg");
206                 }else if(fileName.toLowerCase().endsWith(".psd")){
207                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/PSD.jpg");
208                 }else if(fileName.toLowerCase().endsWith(".ai")){
209                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/AI.jpg");
210                 }else if(fileName.toLowerCase().endsWith(".cdr")){
211                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/CDR.jpg");
212                 }else if(fileName.toLowerCase().endsWith(".zip")){
213                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/ZIP.jpg");
214                 }else if(fileName.toLowerCase().endsWith(".pdf")){
215                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/PDF.jpg");
216                 }else if(fileName.toLowerCase().endsWith(".png")){
217                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/PNG.jpg");
218                 }else if(fileName.toLowerCase().endsWith(".rar")){
219                     QywFile.setFilepreview("http://www.wodexiangce.cn/images/type/RAR.jpg");
220                 }
221                 QywFile.setUserid(userid);
222                 FileUtils.fileCompose(QywFile, chunkFileMap);//合並文件
223                 File file = new File(QywFile.getFilepath());
224                 if(!file.exists()){
225                     System.out.println("文件合並失敗:"+QywFile.getFilepath());
226                     response.getWriter().write(JSONObject.fromObject("{\"status\":\"error\",'errstr':'文件合並失敗'}").toString());
227                     return;
228                 }
229                 
230                 //把文件路徑保存到數據庫
231                 QywFileService fileService  = (QywFileService)wac.getBean("qywFileService");
232                 Long fileid = (Long)fileService.saveQywFile(QywFile);
233                 System.out.println("文件保存成功:"+fileid);
234                 
235                 response.getWriter().write(JSONObject.fromObject("{\"status\":\"success\",'url':'"+QywFile.getFiledownurl()+"'}").toString());
236             }else{
237                 System.out.println("分片數量不正確,實際分片數量:"+chunkFileMap.size()+" 總分片數量:"+chunks);
238                 response.getWriter().write(JSONObject.fromObject("{\"status\":\"error\",'errstr':'分片數量不正確'}").toString());
239             }
240         } catch (Exception e) {
241             e.printStackTrace();
242             System.err.println("文件合並失敗:"+e.getMessage());
243             response.getWriter().write(JSONObject.fromObject("{\"status\":\"error\",'errstr':'文件合並失敗'}").toString());
244         }
245     }
246     
247     /**
248      * 指定臨時目錄
249      * @return
250      */
251     private String getTempFilePath(){
252         return "/site/xxxxxx/temp";
253     }
254     
255 }

補充:涉及到的實體類和工具類

  1 package com.wodexiangce.persistence.model;
  2 
  3 import java.io.Serializable;
  4 import java.math.BigDecimal;
  5 import java.util.Date;
  6 
  7 
  8 public class WdxcQywFiles implements Serializable{
  9 
 10     private static final long serialVersionUID = 1L;
 11 
 12     private long id;
 13     
 14     private long userid;
 15     
 16     private String filename;
 17     
 18     private String filepreview;
 19     
 20     private String filepath;
 21     
 22     private int deleted;
 23     
 24     private Date deletedtime;
 25     
 26     private Date createtime;
 27     
 28     private String filedownurl;
 29     
 30     private BigDecimal filesize;
 31 
 32     public long getId() {
 33         return id;
 34     }
 35     public void setId(long id) {
 36         this.id = id;
 37     }
 38     public long getUserid() {
 39         return userid;
 40     }
 41     public void setUserid(long userid) {
 42         this.userid = userid;
 43     }
 44     public String getFilename() {
 45         return filename;
 46     }
 47     public void setFilename(String filename) {
 48         this.filename = filename;
 49     }
 50     public String getFilepreview() {
 51         return filepreview;
 52     }
 53     public void setFilepreview(String filepreview) {
 54         this.filepreview = filepreview;
 55     }
 56     public String getFilepath() {
 57         return filepath;
 58     }
 59     public void setFilepath(String filepath) {
 60         this.filepath = filepath;
 61     }
 62     /**
 63      * 刪除狀態 0:正常 1:已刪除
 64      * @return
 65      */
 66     public int getDeleted() {
 67         return deleted;
 68     }
 69     public void setDeleted(int deleted) {
 70         this.deleted = deleted;
 71     }
 72     public Date getDeletedtime() {
 73         return deletedtime;
 74     }
 75     public void setDeletedtime(Date deletedtime) {
 76         this.deletedtime = deletedtime;
 77     }
 78     public Date getCreatetime() {
 79         return createtime;
 80     }
 81     public void setCreatetime(Date createtime) {
 82         this.createtime = createtime;
 83     }
 84     /**
 85      * 文件下載URL前綴(需拼寫分片名稱)
 86      * @return
 87      */
 88     public String getFiledownurl() {
 89         return filedownurl;
 90     }
 91     public void setFiledownurl(String filedownurl) {
 92         this.filedownurl = filedownurl;
 93     }
 94     /**
 95      * 文件大小
 96      * @return
 97      */
 98     public BigDecimal getFilesize() {
 99         return filesize;
100     }
101     public void setFilesize(BigDecimal bigDecimal) {
102         this.filesize = bigDecimal;
103     }
104 }
  1 package com.wodexiangce.util;
  2 
  3 import java.io.File;
  4 import java.io.FileInputStream;
  5 import java.io.FileOutputStream;
  6 import java.io.InputStream;
  7 import java.lang.reflect.Method;
  8 import java.math.BigDecimal;
  9 import java.nio.MappedByteBuffer;
 10 import java.nio.channels.FileChannel;
 11 import java.security.AccessController;
 12 import java.security.MessageDigest;
 13 import java.security.PrivilegedAction;
 14 import java.util.Map;
 15 
 16 import com.wodexiangce.persistence.model.WdxcQywFiles;
 17 
 18 /**
 19  * 文件處理工具類
 20  */
 21 public class FileUtils {
 22     /**
 23      * 保存文件流到文件,同時返回文件MD5
 24      * @param inputStream
 25      * @param file
 26      */
 27     public static String copyInputStreamToFile(InputStream inputStream,File file){
 28         MessageDigest md = null;
 29         try {
 30             if(inputStream == null || inputStream == null) {
 31                 return null;
 32             }
 33             md = MessageDigest.getInstance("MD5");
 34             byte[] b = new byte[102400];//set b 100Kb byte.
 35             int n = inputStream.read(b);
 36             int totalBytes = n;
 37             FileOutputStream fos = new FileOutputStream(file);
 38             while (n > -1) {
 39                 fos.write(b, 0, n);
 40                 fos.flush();
 41                 n = inputStream.read(b);
 42                 totalBytes += n;
 43             }
 44             fos.close();
 45             inputStream.close();
 46             System.out.println("文件總大小:"+totalBytes);
 47             //生成文件MD5值
 48             FileInputStream in = new FileInputStream(file); 
 49             //文件內存映射,提高讀寫超大文件可能和速度,但會造成文件鎖定不可操作。
 50             MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());  
 51             md.update(byteBuffer);
 52             clean(byteBuffer);
 53             
 54             byte[] encrypt = md.digest();
 55             StringBuffer sb = new StringBuffer();
 56             for (int i = 0; i < encrypt.length; i++) {
 57                 String hex = Integer.toHexString(0xff & encrypt[i]);
 58                 if (hex.length() == 1)
 59                     sb.append('0');
 60                 sb.append(hex);
 61             }
 62             in.close();
 63             return sb.toString();
 64         } catch (Exception e) {
 65             e.printStackTrace();
 66             return null;
 67         }
 68     }
 69     
 70     /**
 71      * 文件合並
 72      * @param QywFile
 73      * @param chunkFileMap
 74      */
 75     public static void fileCompose(WdxcQywFiles QywFile,Map<String,File> chunkFileMap){
 76         String path = QywFile.getFilepath();
 77         File mainFile = new File(path);
 78         if(!mainFile.getParentFile().exists()){
 79             mainFile.getParentFile().mkdirs();
 80         }
 81         try {
 82             FileChannel out = new FileOutputStream(mainFile).getChannel();
 83             FileChannel in = null;
 84             long start = System.currentTimeMillis();
 85             for(int i=0;i<chunkFileMap.size();i++){
 86                 File file = chunkFileMap.get(String.valueOf(i));
 87                 System.out.println("file="+file.getName());
 88                 in = new FileInputStream(file).getChannel();
 89                 MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
 90                 out.write(buf);
 91                 in.close();
 92                 FileUtils.clean(buf);
 93             }
 94             System.out.println("文件大小:"+mainFile.length()/1024/1024+" M");
 95             QywFile.setFilesize(new BigDecimal(mainFile.length()));
 96             long end = System.currentTimeMillis();
 97             System.out.println("常規方法合並耗時:"+(end-start)/1000+" 秒");
 98         }catch (Exception e) {
 99             e.printStackTrace();
100         }
101     }
102     
103     
104     @SuppressWarnings("unchecked")
105     public static void clean(final Object buffer) throws Exception {
106          AccessController.doPrivileged(new PrivilegedAction() {
107              public Object run() {
108              try {
109                 Method getCleanerMethod = buffer.getClass().getMethod("cleaner",new Class[0]);
110                 getCleanerMethod.setAccessible(true);
111                 sun.misc.Cleaner cleaner =(sun.misc.Cleaner)getCleanerMethod.invoke(buffer,new Object[0]);
112                 cleaner.clean();
113              } catch(Exception e) {
114                 e.printStackTrace();
115              }
116                 return null;}
117           });
118      }
119 }

 


免責聲明!

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



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