文件上传插件WebUploader的使用

文件上传插件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规范组织代码,清晰明了,方便高级玩家扩展。

资源介绍:

// SWF文件,当使用Flash运行时需要引入。
Uploader.swf
// 完全版本。
webuploader.js
webuploader.min.js
// 只有Flash实现的版本。
webuploader.flashonly.js
webuploader.flashonly.min.js
// 只有Html5实现的版本。
webuploader.html5only.js
webuploader.html5only.min.js
// 去除图片处理的版本,包括HTML5和FLASH.
webuploader.withoutimage.js
webuploader.withoutimage.min.js

使用方法:

可以参考  WebUploader API文档  

接下来以图片上传实例,讲解如何使用WebUploader。

我们首先将css和相关js文件加载。

1 <link rel="stylesheet" type="text/css" href="css/webuploader.css">
2 <script src="http://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
3 <script type="text/javascript" src="js/webuploader.min.js"></script> 

然后我们需要准备一个按钮#imgPicker,和一个用来存放添加的文件信息列表的容器#fileList,在body中加入如下代码:

1 <div id="uploadimg">   
2     <div id="fileList" class="uploader-list"></div>   
3     <div id="imgPicker">选择图片</div>  
4 </div>

首先创建Web Uploader实例:

复制代码
 1 var uploader = WebUploader.create({   
 2     auto: true, // 选完文件后,是否自动上传   
 3     swf: 'js/Uploader.swf', // swf文件路径   
 4     server: '/uploadServlet?methodName=fileupload', // 文件接收服务端   
 5     pick: '#imgPicker', // 选择文件的按钮。可选   
 6     // 只允许选择图片文件。   
 7     accept: {   
 8         title: 'Images',   
 9         extensions: 'gif,jpg,jpeg,bmp,png',   
10         mimeTypes: 'image/*'  
11     }  
12 }); 
复制代码

接着监听fileQueued事件,即当有文件添加进来的时候,通过uploader.makeThumb来创建图片预览图。

复制代码
 1 uploader.on( 'fileQueued', function( file ) {   
 2     var $list = $("#fileList"), //获取文件列表 
 3     $li = $(
 4         '<div id="' + file.id + '" class="file-item thumbnail">' +   
 5         '<img>' +   
 6         '<div class="info">' + file.name + '</div>' +   
 7         '</div>'
 8     ),   
 9     $img = $li.find('img');
10     $list.append( $li ); // $list为容器jQuery实例    
11     // 创建缩略图   
12     uploader.makeThumb( file, function( error, src ) {   
13         if ( error ) {   
14             $img.replaceWith('<span>不能预览</span>');   
15             return;   
16         }      
17         $img.attr( 'src', src );//设置预览图
18     }, 100, 100 ); //100x100为缩略图尺寸  
19 }); 
复制代码

最后是上传状态提示了:
文件上传过程中对应uploadProgress事件。

复制代码
 1 // 文件上传过程中创建进度条实时显示。
 2 uploader.on( 'uploadProgress', function( file, percentage ) {   
 3     var $li = $( '#'+file.id ),   
 4     $percent = $li.find('.progress span');      
 5     //避免重复创建   
 6     if ( !$percent.length ) {   
 7         $percent = $('<p class="progress"><span></span></p>').appendTo( $li ).find('span');   
 8     }
 9     $percent.css( 'width', percentage * 100 + '%' );  
10 });
复制代码

文件上传成功对应uploadSuccess事件。

1 // 文件上传成功,给item添加成功class, 用样式标记上传成功。  
2 uploader.on( 'uploadSuccess', function( file, res ) {
3     console.log(res.filePath);//这里可以得到上传后的文件路径
4     $( '#'+file.id ).addClass('upload-state-done');  
5 }); 

文件上传失败对应uploadError事件。

复制代码
 1 // 文件上传失败,显示上传出错。 
 2 uploader.on( 'uploadError', function( file ) {   
 3     var $li = $( '#'+file.id ),   
 4     $error = $li.find('div.error');      
 5     // 避免重复创建   
 6     if ( !$error.length ) {   
 7         $error = $('<div class="error"></div>').appendTo( $li );   
 8     }     
 9     $error.text('上传失败');  
10 });
复制代码

文件上传完成对应uploadComplete事件。

1 // 完成上传,成功或者失败,先删除进度条。
2 uploader.on( 'uploadComplete', function( file ) {   
3     $( '#'+file.id ).find('.progress').remove();  
4 });

到这里,我们就实现了一个简单的图片上传实例,点击“选择图片”会弹出文件选择对话框,当选择图片后,即进入上传图片流程,会将图片对应的缩略图现实在列表里。

=====================================================================================

接下来贴上文件分片上传的完整代码:

先引入相关js文件:

<script type="text/javascript" src="http://www.qiyinwang.com.cn/js/newperson/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="http://www.qiyinwang.com.cn/js/webuploader/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+'" οnclick="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

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 }
复制代码

 

分类:  【08】JS/JQUERY
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值