• chunkupload 文件上传断点续传组件(java)


     chunkupload简介

         

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

         chunkupload实现如下功能:

         ·  实现断点续传

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

         ·  线程安全

         ·  同一物理机下进程安全

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

         ·  内存占用小

         ·  高效稳定,高可用

         ·  易集成,无第三方依赖

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

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

         为了尽可能提升用户体验,chunkupload在客户端的技术选型有些激进,采用了许多先进的技术,比如:web worker、XMLHttpRequest数据传送进度、FileReader、file slice等,所以对浏览器兼容性会有一定影响,在确定使用chunkupload前请务必仔细斟酌!

    chunkupload集成

    服务端

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

         1.引用chunkupload.1.0.jar

         2.在项目web.xml中配置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",   //切片md5
    12 
    13                 "n": 1  //切片序号
    14 
    15             },
    16 
    17             {
    18 
    19                 "md5": "48357caa7607a636e858315e1b0216d5",
    20 
    21                 "n": 2
    22 
    23             },
    24 
    25             {
    26 
    27                 "md5": "a23c6ab7104d2ce4ae3c1624ea7eab55",
    28 
    29                 "n": 3
    30 
    31             },
    32 
    33             {
    34 
    35                 "md5": "3eb29f6241d6fbb35cc715fff2b9ab91",
    36 
    37                 "n": 4
    38 
    39             },
    40 
    41             {
    42 
    43                 "md5": "120ddc96b878a63adcd7835cbac0c95c",
    44 
    45                 "n": 5
    46 
    47             }
    48 
    49         ],
    50 
    51         "chunkNum": 5,   //切片数量
    52 
    53         "md5": "f1154ca6fab7f3628927c1268f3570fd",   //文件md5
    54 
    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="/static/lib/dawn/dawn.js"></script>
     70     <script src="/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="/static/lib/dawn/dawn.js"></script>
    74     <script src="/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="/static/lib/dawn/dawn.js"></script>
    74     <script src="/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
  • 相关阅读:
    SQL Server CheckPoint的几个误区
    MongoDB集群与LBS应用系列(一)
    也来“玩”Metro UI之磁贴
    Python基础:函数式编程
    ASP.NET MVC 用户登录Login
    巧用 .NET 中的「合并运算符」获得 URL 中的参数
    Razor.js,基于JavaScript的Razor实现
    大话数据结构-排序
    hdu 1498
    3.7 检测两个表中是否有相同的数据
  • 原文地址:https://www.cnblogs.com/iyangyuan/p/5930421.html
Copyright © 2020-2023  润新知