• 5.2 SpringBoot实现断点续传功能 > 我的程序猿之路:第四十二章


    功能使用webuploader组件分片下载文件 文档地址: http://fex.baidu.com/webuploader/document.html

     http://fex.baidu.com/webuploader/download.html中下载

    用到的是:

                  Uploader.swf

                  webuploader.css

                  webuploader.min.js

    我的目录

    需要jar包:

            commons-fileupload-1.3.3.jar

            commons-io-2.5.jar

        导入jar包方法:Shift+Ctrl+Alt+s

     

        按步骤导入   jar包。。。

    webUploader.java

      1 package com.example.demo3.upLoader;
      2 
      3 import org.apache.commons.fileupload.FileItem;
      4 import org.apache.commons.fileupload.FileUploadException;
      5 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
      6 import org.apache.commons.fileupload.servlet.ServletFileUpload;
      7 import org.apache.commons.io.FileUtils;
      8 import org.springframework.stereotype.Controller;
      9 import org.springframework.web.bind.annotation.RequestMapping;
     10 
     11 import javax.servlet.ServletException;
     12 import javax.servlet.http.HttpServletRequest;
     13 import javax.servlet.http.HttpServletResponse;
     14 import java.io.File;
     15 import java.io.FileInputStream;
     16 import java.io.FileOutputStream;
     17 import java.io.IOException;
     18 import java.util.HashMap;
     19 import java.util.List;
     20 
     21 @RequestMapping("uploader.do")
     22 @Controller
     23 public class WebUpLoaderController {
     24     private static final long serialVersionUID = 1L;
     25     @RequestMapping("loader")
     26     public void douploader(HttpServletRequest request, HttpServletResponse response)
     27             throws Exception {
     28         String fileName = request.getParameter("fileName");
     29         String fileMd5 = request.getParameter("fileMd5");
     30         String chunk = request.getParameter("chunk");
     31         String chunkSize = request.getParameter("chunkSize");
     32         String guid = request.getParameter("guid");
     33 
     34         String path = ("C:/upload/uploads");
     35         File checkFile = new File(path+"/"+guid+"/"+chunk);
     36 
     37         response.setContentType("text/html;charset=utf-8");
     38 
     39         //检查文件是否存在,且大小是否一致
     40         if(checkFile.exists() && checkFile.length()==Integer.parseInt(chunkSize)){
     41             //上传过
     42 
     43                 response.getWriter().write("{"ifExist":1}");
     44 
     45         }else{
     46             //没有上传过
     47 
     48                 response.getWriter().write("{"ifExist":0}");
     49 
     50         }
     51 
     52     }
     53     @RequestMapping("loader2")
     54     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     55 
     56          String path = "C:\upload\uploads";
     57          String guid = request.getParameter("guid");
     58          String fileName = request.getParameter("fileName");
     59         /**
     60          * 进行文件合并
     61          */
     62         File file = new File(path+"/"+guid);
     63         new File("C:\upload\upload"+"/"+guid).mkdirs();
     64         /**
     65          * 进行文件合并
     66          */
     67         File newFile = new File("C:\upload\upload"+"/"+guid+"/"+fileName);
     68         FileOutputStream outputStream = new FileOutputStream(newFile, true);//文件追加写入
     69         byte[] byt = new byte[10*1024*1024];
     70         int len;
     71         FileInputStream temp = null;//分片文件
     72         File[] childs = new File(path+"/"+guid).listFiles();
     73         boolean boo = false;
     74         if(childs!=null) {
     75             for (int i = 0; i < childs.length; i++) {
     76                 temp = new FileInputStream(childs[i]);
     77                 while ((len = temp.read(byt)) != -1) {
     78                     //System.out.println(len);
     79                     outputStream.write(byt, 0, len);
     80                 }
     81                 temp.close();
     82                 boo=true;
     83             }
     84         }
     85         /**
     86          * 当所有追加写入都写完  才可以关闭流
     87          */
     88         outputStream.close();
     89         if(temp!=null){
     90             temp.close();
     91         }
     92         if(boo) {
     93             delFolder(path + "/" + guid);
     94         }
     95 
     96     }
     97     @RequestMapping("loader1")
     98     protected void doupload(HttpServletRequest request, HttpServletResponse response)
     99             throws ServletException, IOException {
    100         String path = "C:\upload\uploads";
    101         //DiskFileItemFactory factory = new DiskFileItemFactory();
    102         // 2、创建一个文件上传解析器
    103         ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
    104 
    105         // 设置单个文件的最大上传值
    106         upload.setFileSizeMax(15*1024*1024L);
    107         // 设置整个request的最大值
    108         upload.setSizeMax(15*1024*1024L);
    109         // 解决上传文件名的中文乱码
    110         upload.setHeaderEncoding("UTF-8");
    111         //  fileUpload.setProgressListener(listener);
    112         // 3、判断提交上来的数据是否是上传表单的数据
    113         if (!ServletFileUpload.isMultipartContent(request)) {
    114             return;
    115         }
    116         // 4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
    117         List<FileItem> list = null;
    118         try {
    119 
    120             list = upload.parseRequest(request);
    121             // 获得文件:
    122             //    MultipartFile file = multipartRequest.getFile(" file ");
    123         } catch (FileUploadException e) {
    124             e.printStackTrace();
    125         }
    126 
    127         HashMap<String, String> map = new HashMap<String, String>();
    128 
    129         for (FileItem item : list) {
    130             if (item.isFormField()) {
    131                 String name = item.getFieldName();
    132                 // 解决普通输入项的数据的中文乱码问题
    133                 String value = item.getString("UTF-8");
    134 
    135                 map.put(name, value);// 放入map集合
    136             } else {
    137                 /**
    138                  * 文件上传
    139                  */
    140 
    141                 File fileParent = new File(path + "/" + map.get("guid"));//以guid创建临时文件夹
    142                 if (!fileParent.exists()) {
    143                     fileParent.mkdir();
    144                 }
    145 
    146 
    147                 String filename = item.getName();
    148                 if (filename == null || filename.trim().equals("")) {
    149                     continue;
    150                 }
    151                 // 注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:
    152                 // c:a1.txt,而有些只是单纯的文件名,如:1.txt
    153                 // 处理获取到的上传文件的文件名的路径部分,只保留文件名部分
    154                 filename = filename.substring(filename.lastIndexOf("\") + 1);
    155 
    156                 //创建文件
    157                 File file;
    158                 if (map.get("chunks") != null) {
    159                     file = new File(fileParent, map.get("chunk"));
    160                 } else {
    161                     file = new File(fileParent, "0");
    162                 }
    163 
    164                 //copy
    165                 FileUtils.copyInputStreamToFile(item.getInputStream(), file);
    166                 item.getInputStream().close();
    167             }
    168         }
    169     }
    170     /***
    171      * 删除文件夹
    172      */
    173     public  static void delFolder(String paths) {
    174         try {
    175             delAllFile(paths); // 删除完里面所有内容
    176             String filePath = paths;
    177             filePath = filePath.toString();
    178             File myFilePath = new File(filePath);
    179             myFilePath.delete(); // 删除空文件夹
    180         } catch (Exception e) {
    181             e.printStackTrace();
    182         }
    183     }
    184     /***
    185      * 删除指定文件夹下所有文件
    186      * @return
    187      */
    188     public static  boolean delAllFile(String paths) {
    189         boolean flag = false;
    190         //判断这个路径名是否存在
    191         File file = new File(paths);
    192         if (!file.exists()) {
    193             return flag;
    194         }
    195         //是否是一个目录
    196         if (!file.isDirectory()) {
    197             return flag;
    198         }
    199         //返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。
    200         String[] tempList = file.list();
    201         File temp = null;
    202         for (int i = 0; i < tempList.length; i++) {
    203             //paths是否以File.separator结束  方便不同平台下使用
    204             if (paths.endsWith(File.separator)) {
    205                 //取一个 删除一个 ...
    206                 temp = new File(file + tempList[i]);
    207             } else {
    208                 //取一个 删除一个 ...
    209                 temp = new File(file + File.separator + tempList[i]);
    210             }
    211             //是否是一个标准文件。
    212             if (temp.isFile()) {
    213                 //是的话删除
    214                 temp.delete();
    215                 flag = true;
    216             }
    217             //是否是一个目录,如果是的话先删除(delAllFile)目录下的文件,然后再删除空文件夹
    218             if (temp.isDirectory()) {
    219                 delAllFile(paths + "/" + tempList[i]);// 先删除文件夹里面的文件
    220                 delFolder(paths + "/" + tempList[i]);// 再删除空文件夹
    221                 flag = true;
    222             }
    223         }
    224         return flag;
    225     }
    226 }

         webUploader.html

      1 <!DOCTYPE html>
      2 <html lang="en" xmlns:th="http://www.thymeleaf.org">
      3 <head>
      4     <meta charset="UTF-8">
      5     <title>Upload Page</title>
      6     <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
      7     <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
      8 
      9     <script type="text/javascript" src="/upload/jquery-1.7.2.js"></script>
     10     <script type="text/javascript" src="/upload/webuploader.min.js"></script>
     11     <link href="/upload/webuploader.css" type="css/text" />
     12     <script type="text/javascript" src="/upload/jquery-2.0.0.min.js"></script>
     13     <script type="text/javascript" src="/upload/jquery-ui.js"></script>
     14     <link href="/upload/bootstrap.min.css" rel="stylesheet" media="screen">
     15     <script type="text/javascript" src="/upload/bootstrap.min.js"></script>
     16 
     17 </head>
     18 
     19 <body>
     20   <div style="margin: 22px 22px 22px 1px;">
     21       <div id="picker" class="form-control-focus">选择文件</div><br/>
     22       <p>允许上传文件类型:<b>.zip .dmp .rar .js .css .xml .7z .ico .pdf .ppt .pptx .xap .xpi .swf .apk .cdf .gif .tar .gz .sh .bmp .jpg</b><br />
     23       一次上传文件大小限制不小于:<b>50</b>MB</p>
     24 
     25       <table id="thelist" class="table table-bordered;uploader-list">
     26           <thead>
     27           <tr>
     28               <th>文件名</th>
     29               <th>文件大小</th>
     30               <th>状态</th>
     31               <th>操作</th>
     32           </tr>
     33           </thead>
     34       </table>
     35 
     36   </div>
     37   <button id="btnSync" type="button" class="btn btn-warning">开始同步</button>
     38 
     39 
     40 
     41 
     42 
     43   <script>
     44       var fileMd5;  //文件唯一标识
     45 
     46       /******************下面的参数是自定义的*************************/
     47       var fileName;//文件名称
     48       var oldJindu;//如果该文件之前上传过 已经上传的进度是多少
     49       var count=0;//当前正在上传的文件在数组中的下标,一次上传多个文件时使用
     50       var filesArr=new Array();//文件数组:每当有文件被添加进队列的时候 就push到数组中
     51       var map={};//key存储文件id,value存储该文件上传过的进度
     52       WebUploader.Uploader.register({
     53               "before-send-file":"beforeSendFile",//整个文件上传前
     54               "before-send":"beforeSend",  //每个分片上传前
     55               "after-send-file":"afterSendFile",  //分片上传完毕
     56           },
     57           {
     58               //时间点1:所有分块进行上传之前调用此函数
     59               beforeSendFile:function(file){
     60               //    alert('----');
     61                   var deferred = WebUploader.Deferred();
     62                   //1、计算文件的唯一标记fileMd5,用于断点续传  如果.md5File(file)方法里只写一个file参数则计算MD5值会很慢 所以加了后面的参数:10*1024*1024
     63                   (new WebUploader.Uploader()).md5File(file,0,10*1024*1024).progress(function(percentage){
     64                       $('.'+file.id ).find('p.state').text('正在读取文件信息...');
     65                   })
     66                       .then(function(val){
     67                           $('.'+file.id ).find("p.state").text("成功获取文件信息...");
     68                           fileMd5=val;
     69                           uploader.options.formData.guid = fileMd5;
     70                           console.log("fileMd5:"+fileMd5);
     71                           //获取文件信息后进入下一步
     72                           deferred.resolve();
     73                       });
     74 
     75                   fileName=file.name; //为自定义参数文件名赋值
     76                   return deferred.promise();
     77               },
     78               //时间点2:如果有分块上传,则每个分块上传之前调用此函数
     79               beforeSend:function(block){
     80              //     alert('-******-');
     81                   var deferred = WebUploader.Deferred();
     82                   $.ajax({
     83                       type:"POST",
     84                       url:"/uploader.do/loader",  //ajax验证每一个分片
     85                       data:{
     86                           fileName : fileName,
     87                           fileMd5:fileMd5,  //文件唯一标记
     88                           chunk:block.chunk,  //当前分块下标
     89                           chunkSize:block.end-block.start,//当前分块大小
     90                           guid: uploader.options.formData.guid
     91                       },
     92                       cache: false,
     93                       async: false,  // 与js同步
     94                       timeout: 1000,
     95                       dataType:"json",
     96                       success:function(response){
     97                           console.log(block.chunk+"--"+response.ifExist);
     98                           if(response.ifExist){
     99                               //分块存在,跳过
    100                               deferred.reject();
    101                           }else{
    102                             //  alert("ss11")
    103                               //分块不存在或不完整,重新发送该分块内容
    104                               deferred.resolve();
    105                           }
    106                       }
    107                   });
    108 
    109                   this.owner.options.formData.fileMd5 = fileMd5;
    110                   deferred.resolve();
    111                   return deferred.promise();
    112               },
    113               //时间点3:所有分块上传成功后调用此函数
    114               afterSendFile:function(){
    115                 //  alert('-***2222**-');
    116                   //如果分块上传成功,则通知后台合并分块
    117                   $.ajax({
    118                       type:"POST",
    119                       url:"${ctx}/mergeOrCheckChunks.do?param=mergeChunks",  //ajax将所有片段合并成整体
    120                       data:{
    121                           fileName : fileName,
    122                           fileMd5:fileMd5,
    123                       },
    124                       success:function(data){
    125                           count++; //每上传完成一个文件 count+1
    126                           ;                      if(count<=filesArr.length-1){
    127                               uploader.upload(filesArr[count].id);//上传文件列表中的下一个文件
    128                           }
    129                           //合并成功之后的操作
    130                       }
    131                   })
    132               }
    133           });
    134       var uploader = WebUploader.create({
    135 
    136           // swf文件路径
    137           swf : 'upload/Uploader.swf',
    138           // 文件接收服务端。
    139           server : '/uploader.do/loader1',
    140           // 选择文件的按钮。可选。
    141           // 内部根据当前运行是创建,可能是input元素,也可能是flash.
    142           pick : '#picker',
    143           chunked: true,  //分片处理
    144           chunkSize: 10 * 1024 * 1024, //每片5M
    145           threads:3,//上传并发数。允许同时最大上传进程数。
    146           // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
    147           resize : false
    148       });
    149 
    150       function removeSection(e) {
    151        //   alert("当前第几行:"+e.parentElement.parentElement.rowIndex)
    152           //alert("del:":+e.parentElement.parentElement.removeNode(true))
    153           //var a = e.parentElement.parentElement.rowIndex;
    154           //var table=document.getElementById("thelist");
    155           //var objt = $(obj);.remove();
    156           //    uploader.removeFile( file );
    157           var tr=e.parentNode.parentNode;
    158           tr.parentNode.removeChild(tr);
    159           //e.parentNode.removeChild(e.parentElement.parentElement.rowIndex);
    160           //table.deleteRow(e.parentElement.parentElement.rowIndex);
    161           //alert(table)
    162           //var len=table.rows.length;
    163           //alert(len)
    164           //      alert("ss"+e.id+"==="+e.name)
    165           //    $(e).parents(e.id).remove();
    166           //    $('[data-spy="scroll"]').each(function () {
    167           //        var $spy = $(this).scrollspy('refresh')
    168           //        });
    169       }
    170       // 当有文件被添加进队列的时候
    171       uploader.on('fileQueued', function(file) {
    172           //    alert(file.size)
    173           var fileName = file.name
    174           if (fileName.indexOf('#') >= 0) {
    175               alert('文件名中不能有井号');
    176               return false;
    177           }
    178           if (fileName.indexOf('+') >= 0) {
    179               alert('文件名中不能有加号');
    180               return false;
    181           }
    182           if (fileName.indexOf(' ') >= 0) {
    183               alert('文件名中不能有空格');
    184               return false;
    185           }
    186           var reg = /(.zip|.dmp|.rar|.js|.css|.xml|.7z|.ico|.pdf|.ppt|.pptx|.xap|.xpi|.swf|.apk|.cdf|.gif|.tar|.gz|.sh|.bmp|.jpg)$/ig;
    187           if (!reg.test(fileName)) {
    188               var supportType = reg.toString().replace(//|(|)|\|/ig, '');
    189               supportType = supportType.substring(0, supportType.length - 3);
    190               supportType = supportType.replace(/|/g, ' ');
    191               alert('不支持该上传文件类型!
    支持的文件类型:' + supportType);
    192            //   return false;
    193           }
    194           if (file.size < 50*1024*1024)
    195           {
    196               alert('上传文件大小不能小于' + 50 + 'MB');
    197             //  return false;
    198           }
    199         //  alert(123);
    200           $("#thelist").append(
    201               '<tr class="'+ file.id +'">'
    202               +'<td ><h4 class="info">' + file.name + '</h4></td>'
    203               +'<td><font face="宋体" size=3 color="red">'+ file.size +' </font>'+'字节'+'</td>'
    204               +'<td id="' + file.id + '"><p class="state">等待上传...</p></td>'//onclick="removeSection(this);"
    205               +'<td><input type="button" class="remove-this" name="sss" id="'+ file.id +'"  value="删除" class="btn btn-default"/></td>'
    206               +'</tr>');
    207       });
    208 
    209       uploader.on('uploadProgress', function(file,percentage) {
    210 
    211           var $li = $('#' + file.id),
    212               $percent = $li.find('.progress .progress-bar');
    213 
    214           // 避免重复创建
    215           if (!$percent.length) {
    216               $percent = $('<div id="' + file.id + '" class="progress progress-striped active">' +
    217                   '<div class="progress-bar" style="height:20px; role="progressbar" style=" 0%">' +
    218                   '</div>' +
    219                   '</div>').appendTo($li).find('.progress-bar');
    220           }
    221 
    222           $('.'+file.id ).find('p.state').text('上传中');
    223           $percent.css('width', percentage * 100 + '%');
    224       });
    225 
    226       uploader.on('uploadSuccess', function(file) {
    227           $('.' + file.id).find('p.state').text('已上传');
    228           $.post("/uploader.do/loader2", { "guid": uploader.options.formData.guid,fileName:file.name},
    229               function(data){
    230               }, "json");
    231       });
    232 
    233       uploader.on('uploadError', function(file) {
    234           $('.' + file.id).find('p.state').text('上传出错');
    235       });
    236 
    237       uploader.on('uploadComplete', function(file) {
    238           $('.' + file.id).find('.progress').fadeOut();
    239       });
    240 
    241       uploader.on( 'beforeFileQueued', function( file ) {
    242 
    243        //   alert(file.size);
    244 
    245       });
    246 
    247       $("#btnSync").on('click', function() {
    248           if ($(this).hasClass('disabled')) {
    249               return false;
    250           }
    251           console.log("get fileMd5:"+fileMd5);
    252 
    253           uploader.upload();
    254 
    255       });
    256   </script>
    257 </body>
    258 </html>

    application.propertise

    #文件分片上传关闭multipart,不然的话request被预编译,文件解析器解析请求为空
    spring.servlet.multipart.enabled=false

    遇到的问题:

    list = upload.parseRequest(request);(74行) list为空
    我用Meclipse可以,idea就不行了,很纳闷。
    网上找了很多,知道request被预编译,但是说法大多数struts配置影响的还有一些其他方法,
    看来下,卧槽,那么复杂,我这么lazy,算了,再找找其他的。
    最后在一片博文找到了方法,在application.propertise中
    关闭multipart,问题解决
    spring.servlet.multipart.enabled=false


  • 相关阅读:
    Android开发 View_自定义快速索引侧边栏 SideBarView
    Android开发 PopupWindow开发的一些例子
    Android开发 Fragment里监听返回键
    AndroidStudio 清除项目里无用的资源
    Android开发 SingleLiveEvent解决LiveData或者MutableLiveData多次回调的问题
    字母排列城市列表资源
    Android开发 GridView详解
    Android开发 NestedScrollView嵌套RecyclerView导致的UI加载慢的问题
    Android开发 跳转指定应用商城评分
    Android开发 Activity生命周期详解
  • 原文地址:https://www.cnblogs.com/fanyuyi-boke/p/fanyuyi_webUploader.html
Copyright © 2020-2023  润新知