你的位置:首页 > Java教程

[Java教程]chunkupload 文件上传断点续传组件(java)


 chunkupload简介

     

     chunkupload是一款基于java语言的断点续传组件,针对文件上传,非文件下载,集成方便,使用简单。

     chunkupload实现如下功能:

 

     ·  实现断点续传

     ·  对于同一个文件,允许多用户同时上传,并且上传的用户越多,上传越快

     ·  线程安全

     ·  同一物理机下进程安全

     ·  文件自动切片,支持合并

     ·  内存占用小

     ·  高效稳定,高可用

     ·  易集成,无第三方依赖

 

     chunkupload只关注文件上传,并没有安全机制,开发者需要自行设计安全控制策略,防范用户上传非法文件,chunkupload默认上传的文件是安全的。

     chunkupload功能完备,服务端和客户端无缝衔接,开发者只需关注自身业务和UI展现即可。

     为了尽可能提升用户体验,chunkupload在客户端的技术选型有些激进,采用了许多先进的技术,比如:web worker、

 

chunkupload集成

 

服务端

 

     chunkupload服务端运行需要JRE7或更高版本,无任何第三方依赖。

 

     1.引用chunkupload.1.0.jar

     2.在项目web.中配置chunkupload servlet

 

1 <servlet>2   <servlet-name>ChunkUpload</servlet-name>3   <servlet-class>com.iyangyuan.chunkupload.servlet.DispatcherServlet</servlet-class>4 </servlet>5 <servlet-mapping>6   <servlet-name>ChunkUpload</servlet-name>7   <url-pattern>/chunkupload/*</url-pattern>8 </servlet-mapping>

 

     强烈建议将chunkupload servlet配置在安全框架之后(比如shiro),mvc框架之前(如springmvc)。

     chunkupload servlet中的拦截路径(url-pattern),如果无特殊需求,无需更改,假如一定要更改,还需要同步更改客户端的配置。

 

 客户端

 

     chunkupload客户端对浏览器的要求比较高,目前已知chrome、firefox浏览器完整支持,360浏览器如果启用webkit内核,应该也没有问题,IE浏览器绝对不支持(想都不要想),其他浏览器未知。

     开发者可能会问,为什么兼容性如此捉襟见肘?

     chunkupload是新时代的产物,它代表了时代的发展方向,它的价值在于提供最先进的技术示范,而不是沉重的历史包袱。

     chunkupload在客户端的实现,无任何第三方依赖,只关注与服务端的逻辑交互,并不干预UI展现,为开发者创造最大的发挥空间。

 

     1.引用dawn.1.0.js,用于在客户端计算文件MD5,dawn.js是chunkupload的一个附属项目,相比未经优化的javascript计算MD5方法,dawn.js将计算效率提升50%左右。

     2.引用chunkupload.1.0.js,此乃chunkupload客户端核心库,封装了所有上传所需的逻辑。

 

 chunkupload使用

 

服务端

 

     服务端需要创建chunkupload.properties配置文件,放置在项目classpath根目录下,也就是大家熟悉的log4j.properties所在目录,配置文件中有如下选项:

 

     ·  root 文件存储路径,相当于根目录,内部还会有chunkupload创建的目录结构;假如同一台物理机配置多个文件上传容器,此项配置应该设置成统一目录,默认为[/data]。

     ·  fileLockCapacity 文件锁缓存容量,一般设置为2048即可,开发者可根据服务器性能自行调整,默认为2048。

     ·  createFile 文件上传完成后,是否立即合并切片,生成完整文件;强烈建议此配置项设置为false,一般情况下,切片无需合并,就算合并,也不需要立即合并;如果设为true,立即合并文件会占用大量服务器资源,并且会造成客户端长时间等待;合并的速度大约100M/S,视服务器具体性能而定,默认为true。

 

     至此,服务端已经可以正常运作了。

 

客户端

 

     默认情况下,客户端无需任何配置。

     假如开发者更改过chunkupload servlet拦截路径,那么chunkupload.1.0.js中的Block.config.api配置也需要做相应的改动,具体情况需要开发者自行斟酌。

 

     ChunkUpload 类

 

     文件上传核心实现类。

 

          实例化

 

          上传组件初始化需要提供目标文件。

 

1 /**2 3  * 实例化ChunkUpload组件4 5  * file 要上传的目标文件对象6 7 */8 9 var cu = new ChunkUpload(file);

 

          upload 方法

 

          upload 方法用来启动文件上传,通过四个异步回调完成上传交互,无返回值。

 

 1 cu.upload({ 2   "success": function(block){ 3     /** 4      * 上传成功回调 5      * 6      * block 对象,块对象 7     */     8   }, 9   "error": function(e){10     /**11      * 上传异常回调12      *13      * e 字符串,异常信息14     */15   },16   "md5Progress": function(n){17     /**18      * 计算文件md5进度回调19      *20      * n 整型,进度数值21     */22   },23   "uploadProgress": function(n){24     /**25      * 上传进度回调26      *27      * n 整型,进度数值28     */29   }30 });

 

          abort方法

 

          abort方法用来中断上传,可以在任意阶段任意时刻中断,无返回值。

 

1 cu.abort();

 

     Block 类

 

     文件控制类。

 

          实例化

 

 1 /** 2  3  * 初始化块对象 4  5  * md5 文件md5,32位 6  7  * size 文件大小,字节 8  9 */10 11 var block = new Block(md5, size);

 

          info方法

 

          获取块(文件)信息,返回javascript对象。

 

1 block.info();

 

          返回示例:

 

 1 { 2  3   "status": 0,  //业务状态,0表示成功 4  5   "data": { //数据域 6  7     "chunks": [ //所有切片信息 8  9       {10 11         "md5": "e114c21f7d9f8ad1a8551225c3d085be",  //切片md512 13         "n": 1 //切片序号14 15       },16 17       {18 19         "md5": "48357caa7607a636e858315e1b0216d5",20 21         "n": 222 23       },24 25       {26 27         "md5": "a23c6ab7104d2ce4ae3c1624ea7eab55",28 29         "n": 330 31       },32 33       {34 35         "md5": "3eb29f6241d6fbb35cc715fff2b9ab91",36 37         "n": 438 39       },40 41       {42 43         "md5": "120ddc96b878a63adcd7835cbac0c95c",44 45         "n": 546 47       }48 49     ],50 51     "chunkNum": 5,  //切片数量52 53     "md5": "f1154ca6fab7f3628927c1268f3570fd",  //文件md554 55     "state": 1,  //文件状态,1为上传完成56 57     "size": 20879935 //文件长度58 59   }60 61 }

 

          delete方法

 

          删除块(文件) ,无返回值。

 

1 block.delete();

 

chunkupload服务端存储珠玑

 

     任何上传的文件都会在服务端进行切片处理,每个切片4M大小。

     通过文件MD5和文件大小,唯一确定一个文件。

     目录分散策略,基于开发者自定义的rootpath,文件MD5前6位,每两位作为一级目录,最后以文件MD5+文件长度作为最终目录,所有文件信息均存储在此目录下。

     假如文件MD5为[071287fffa974b878732a7a17858be36],长度为[20879935],开发者自定义的rootpath[/data],那么生成的目录结构为:[/data/07/12/87/071287fffa974b878732a7a17858be3620879935]。

     chunkupload存储的关于文件的所有信息,均为二进制文件,并且文件名称固定,具体组织如下图:

 

 

chunkupload未来

 

     展望chunkupload,未来无疑是开源的,只不过现在还不是时候,因为作者觉得它还不够完美。

     通过大家的宝贵意见、建议,作者会不断完善、改进chunkupload,等到chunkupload成熟时,也就是开源之日!

     希望大家多多与我交流~

 

chunkupload组件下载

 

     你可以下载如下内容:

 

     · chunkupload.jar

     · chunkupload.js

     · dawn.js

     · 脚手架(集成了chunkupload的空白项目)

 

下载

 

附:客户端使用示例

 

 上传示例

 

 1 <html> 2   <head> 3     <title>ChunkUpload 文件上传示例</title> 4     <meta charset="utf-8"> 5     <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> 6     <style> 7       body{ 8         font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; 9       } 10       body > div { 11         width: 200px; 12         margin: 20px auto 0 auto; 13       } 14       body > div.form { 15         text-align: center; 16       } 17       body > div.form > input { 18         margin-top: 12px; 19         border: 1px dashed #dcdcdc; 20         padding: 4px 8px; 21         cursor: pointer; 22         background-color: transparent; 23         color: #686868; 24         font-family: inherit; 25         outline: none; 26         width: 100%; 27         box-sizing: border-box; 28       } 29       body > div.form > input.button { 30         font-size: 15px; 31         line-height: 29px; 32         padding: 0 10px; 33       } 34       body > div.form > input:hover { 35         background-color: #f5f5f5; 36       } 37       div.info > div{ 38         margin: auto; 39         height: 31px; 40         width: 88px; 41         background: url('data:image/gif;base64,R0lGODlhWAAfAMMBAAAAAP////XcoPDLdfTboP+dzv+z2f+AwP9brfPZoAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFFAAKACwAAAAAWAAfAAAE/xDISau9OOvNd/hgKI5kaZ5oWkqB4L5wLM90bd+4PLB57//A2g7QChqPyNiwmGw6c8undKrj/STULC3qAxQAMKw0IUiYyy6yWf3j5rwGsMsrd5JfdzQelre5bxIFcROCdUBneWx6aHd9UFY1hAaTAJQTRo6LfYl8i0KQMoGCk6Slg0FsnGmdq3uZSpBiAnCmtaWGqK4yiq2eVURzwbS2pBK3hzabezOvLlxyYMPEg5aXWk1uWNLTxaOU10lR2oHckwcHlIWn4Ed/c97ECOfocJWy7EDus/C28udx8HAlaZYP1ItttRAgKEYBGS9NelL10LevnC2BR1RhM3iwAD9uGE5/NBvpg+IcLx5BhvTxiiDBGSYPivJI0xo+JzHfZfjyxNHDGC9/MaFhb0bRmzg5hoqE9EnOpk2XDJhKtarVq1izat3KtSvWDmDDih0LNgIAIfkEBRQACgAsAwAGADEAFgAABJ9QyEmrBMDqzbsFRTZh3pacZQUaogC2qZRQc4oVLLbCaS2XOpyBNdTxYjPf5iYcOp+kmCfqaj6vUClHlFlhr5is9nOpfsGAonGs6lrPUOvR1r3BnYdDEZemskdvWAh5ejs5fyqBV4N5LHKIiXcGCAhORnMmAkoUXpJgkEsFimeYPVsgond+oEtMoq86rJpTIZdBpbIjLBp9spu8W7nCshEAIfkEBRQACgAsAwAFADoAFgAABLdQyEmrAMDqzWXySdhZQJFN2KhS30oWxnmZrtp++IrB2B7XNmAPZogVezKgJidobXbEonSaUjaZE2erOpt6vz+gk5VFSTIlsBdDFTffnRM6qo4Bjkirbl4Hp6VJHTdxaD59BgcHRzx2ei5/dQiJiml3XI5xdGCSiTF0gZhPml8ICIB5oSqQh22pjwWjdaAuOWNjJCWwfZeuWoS5sME9rh4VtxolSMo0vXqWyGHESyOzZ9LHx9LaAhEAIfkEBRQACgAsAgAFAEQAFgAABMtQyEmrvTjrmmT63VYBgGieVIhSQFFO5CpPYWefrfEK7T5fto+g9iMVdKSc75dqOlWZ5NGgoyaXK6HqNvR4vxbjlEoux2ZQcHrL2uXK8DhWdGODo5LSO24GmJlqXSIvemN8Vn5ISVkaaRgxe4dwkXOAGpBGkmQHB1ZHfmeWRYZ8CJydSkiiTJGlpzqGlauDpHEICGRXsrMbrZq5THVPXcJhBbWSu2g0MmLIucona4BSx9YuoaKOzBbbMNi6RtHaRToXoLyO3nfneLw/EQAh+QQFFAAKACwCAAUASwAXAAAE3lDISau9OOtdE/9gKI4aAJDo5qUSUJyTyVJeYgtrfeesa8ACF5C1khSLuEkNZSr8TL4haKdMWpM8o/YDdRp+X6iUg9xer2VR0/ttu2Wh5bFKP5eAPrd+P55W00t2FjAneXtvAG9xGkhzHIRBbIdhiU9QM5gxhZKTb5J9mSAyXZ1tBwdhTolwoZiGkwinqFFPrZmvh7GnP5+haTSinHsICG1ioBdywFo8WRu4pcaZjslqBcKdyGQYv2ZcLtelrCS/3YKia9fql7btgy/HXdofZYHVTD8Xq+78FMjz/TBEAAA7') no-repeat center center; 42         padding: 2px; 43         box-sizing: border-box; 44       } 45       div.info > div > p{ 46         text-align: right; 47         font-size: 12px; 48         margin: 0; 49       } 50     </style> 51   </head> 52   <body> 53     <!-- 表单部分 --> 54     <div class="form"> 55       <input id="bigFile" type="file" placeholder="选择一个文件" /> 56       <input id="execBtn" class="button" type="button" value="上传" /> 57       <input id="cancelBtn" class="button" type="button" value="取消" /> 58     </div> 59     <!-- 上传进度展示 --> 60     <div class="info"> 61       <div> 62         <!-- 提示文本 --> 63         <p id="text"></p> 64         <!-- 执行进度 --> 65         <p id="progress"></p> 66       </div> 67     </div> 68   </body> 69   <script src="http://www.cnblogs.com//static/lib/dawn/dawn.js"></script> 70   <script src="http://www.cnblogs.com//static/lib/chunkupload/chunkupload.js"></script> 71   <script> 72     var fileInput = document.getElementById("bigFile"), 73       execBtn = document.getElementById("execBtn"), 74       cancelBtn = document.getElementById("cancelBtn"), 75       text = document.getElementById("text"), 76       progress = document.getElementById("progress"), 77       cu; 78      79     execBtn.addEventListener("click", function(e) { 80       var file; 81       file = fileInput.files[0]; 82       cu = new ChunkUpload(file); 83       cu.upload({ 84         "success": function(block){ 85           /** 86            * 上传成功后,会返回一个block对象 87            * 通过block对象,可以在一定程度上管理文件,目前支持: 88            * 获取文件信息(参见info_example.html) 89            * 删除文件(参见delete_example.html) 90           */ 91           text.innerText = "上传完成"; 92         }, 93         "error": function(e){ 94           text.innerText = e; 95           progress.innerText = ""; 96         }, 97         "md5Progress": function(n){ 98           text.innerText = "计算MD5"; 99           progress.innerText = n + "%";100         },101         "uploadProgress": function(n){102           text.innerText = "正在上传";103           progress.innerText = n + "%";104         }105       });106     });107 108     cancelBtn.addEventListener("click", function(e) {109       if(cu){110         cu.abort();111         alert("已经取消!");112       }113     });114   </script>115 </html>

View Code

 

获取文件信息示例

 

 1 <html> 2   <head> 3     <title>ChunkUpload 获取文件信息示例</title> 4     <meta charset="utf-8"> 5     <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> 6     <style> 7       body { 8         font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; 9       }10       body > div {11         width: 200px;12         margin: 20px auto 0 auto;13       }14       body > div.form {15         color: #686868;16         font-size: 15px;17         text-align: center;18       }19       body > div.form > p {20         margin: 1em 0 .4em 0;21         text-align: left;22       }23       body > div.form > input {24         background-color: transparent;25         outline: none;26         font-family: inherit;27         font-size: inherit;28         color: inherit;29       }30       body > div.form > input:hover {31         background-color: #f5f5f5;32       }33       body > div.form > input.text {34         padding: 6px 8px;35         border: 1px solid #dcdcdc;36       }37       body > div.form > input.button {38         border: 1px dashed #dcdcdc;39         cursor: pointer;40         padding: 0 10px;41         line-height: 29px;42       }43       div.info{44         width: 400px;45         background-color: #f7faff;46         border: 1px solid #b2d7ff;47         padding: 10px 8px;48       }49       div.info > p{50         margin: 0;51         text-align: left;52         font-size: 14px;53         word-break: break-all;54       }55     </style>56   </head>57   <body>58     <!-- 表单部分 -->59     <div class="form">60       <p>文件md5</p>61       <input id="md5" class="text" type="text" placeholder="文件md5" />62       <p>文件大小(字节)</p>63       <input id="size" class="text" type="text" placeholder="文件大小(字节)" />64       <p></p>65       <input id="execBtn" class="button" type="button" value="获取" />66     </div>67     <!-- 结果展示 -->68     <div class="info">69       <p id="text">70       </p>71     </div>72   </body>73   <script src="http://www.cnblogs.com//static/lib/dawn/dawn.js"></script>74   <script src="http://www.cnblogs.com//static/lib/chunkupload/chunkupload.js"></script>75   <script>76     var execBtn = document.getElementById("execBtn"),77       md5 = document.getElementById("md5"),78       size = document.getElementById("size"),79       text = document.getElementById("text");80     81     execBtn.addEventListener("click", function(e) {82       var block = new Block(md5.value, size.value);83       84       block.info(function(info){85         text.innerText = JSON.stringify(info);86       },function(status, text){87         text.innerText = "哎呀!出错啦," + text;88       });89     });90   </script>91 </html>

View Code

 

删除文件示例

 

 1 <html> 2   <head> 3     <title>ChunkUpload 删除文件示例</title> 4     <meta charset="utf-8"> 5     <meta name="viewport" id="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> 6     <style> 7       body { 8         font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; 9       }10       body > div {11         width: 200px;12         margin: 20px auto 0 auto;13       }14       body > div.form {15         color: #686868;16         font-size: 15px;17         text-align: center;18       }19       body > div.form > p {20         margin: 1em 0 .4em 0;21         text-align: left;22       }23       body > div.form > input {24         background-color: transparent;25         outline: none;26         font-family: inherit;27         font-size: inherit;28         color: inherit;29       }30       body > div.form > input:hover {31         background-color: #f5f5f5;32       }33       body > div.form > input.text {34         padding: 6px 8px;35         border: 1px solid #dcdcdc;36       }37       body > div.form > input.button {38         border: 1px dashed #dcdcdc;39         cursor: pointer;40         padding: 0 10px;41         line-height: 29px;42       }43       div.info{44         width: 400px;45         background-color: #f7faff;46         border: 1px solid #b2d7ff;47         padding: 10px 8px;48       }49       div.info > p{50         margin: 0;51         text-align: left;52         font-size: 14px;53         word-break: break-all;54       }55     </style>56   </head>57   <body>58     <!-- 表单部分 -->59     <div class="form">60       <p>文件md5</p>61       <input id="md5" class="text" type="text" placeholder="文件md5" />62       <p>文件大小(字节)</p>63       <input id="size" class="text" type="text" placeholder="文件大小(字节)" />64       <p></p>65       <input id="execBtn" class="button" type="button" value="删除" />66     </div>67     <!-- 结果展示 -->68     <div class="info">69       <p id="text">70       </p>71     </div>72   </body>73   <script src="http://www.cnblogs.com//static/lib/dawn/dawn.js"></script>74   <script src="http://www.cnblogs.com//static/lib/chunkupload/chunkupload.js"></script>75   <script>76     var execBtn = document.getElementById("execBtn"),77       md5 = document.getElementById("md5"),78       size = document.getElementById("size"),79       text = document.getElementById("text");80   81     execBtn.addEventListener("click", function(e) {82       var block = new Block(md5.value, size.value);83       84       block.delete(function(info){85         text.innerText = JSON.stringify(info);86       },function(status, text){87         text.innerText = "哎呀!出错啦," + text;88       });89     });90   </script>91 </html>

View Code