需求:

文件批量上传,支持断点续传。

文件批量下载,支持断点续传。

使用JS能够实现批量下载,能够提供接口从指定url中下载文件并保存在本地指定路径中。

服务器不需要打包。

支持大文件断点下载。比如下载10G的文件。

PC端全平台支持。Windows,macOS,Linux

全浏览器支持。ie6,ie7,ie8,ie9,ie10,ie11,edge,firefox,chrome,safari

 

1、先将 down2 这个示例和控件下载下来,根据个人的需求放置自己需要的东西就行,全部放到项目里也可以,下面是我自己需要的东西:

 

2、代码部分:分为jsp和servlet部分

1、jsp部分代码:

<scripttype=“text/javascript”>var fileMd5;

  //监听分块上传过程中的三个时间点

  WebUploader.Uploader.register({

    “before-send-file”“beforeSendFile”,

    “before-send”“beforeSend”,

    “after-send-file”“afterSendFile”,

  },

  {

    //时间点1:所有分块进行上传之前调用此函数

    beforeSendFile: function(file) {

      var deferred = WebUploader.Deferred();

      //1、计算文件的唯一标记,用于断点续传

      (new WebUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024).progress(function(percentage) {

        $(‘#item1’).find(“p.state”).text(正在读取文件信息…”);

      }).then(function(val) {

        fileMd5 = val;

        $(‘#item1’).find(“p.state”).text(成功获取文件信息…”);

        //获取文件信息后进入下一步

        deferred.resolve();

      });

      return deferred.promise();

    },

    //时间点2:如果有分块上传,则每个分块上传之前调用此函数

    beforeSend: function(block) {

      var deferred = WebUploader.Deferred();

      $.ajax({

        type: “POST”,

        url: <%=basePath%>Video?action=checkChunk”,

        data: {

          //文件唯一标记

          fileMd5: fileMd5,

          //当前分块下标

          chunk: block.chunk,

          //当前分块大小

          chunkSize: block.end – block.start

        },

        dataType: “json”,

        success: function(response) {

          if (response.ifExist) {

            //分块存在,跳过

            deferred.reject();

          else {

            //分块不存在或不完整,重新发送该分块内容

            deferred.resolve();

          }

        }

      });

      this.owner.options.formData.fileMd5 = fileMd5;

      deferred.resolve();

      return deferred.promise();

    },

    //时间点3:所有分块上传成功后调用此函数

    afterSendFile: function() {

      //如果分块上传成功,则通知后台合并分块

      $.ajax({

        type: “POST”,

        url: <%=basePath%>Video?action=mergeChunks”,

        data: {

          fileMd5: fileMd5,

        },

        success: function(response) {

          alert(上传成功”);

          var path = “uploads/” + fileMd5 + “.mp4”;

          $(“#item1”).attr(“src”, path);

        }

      });

    }

  });

  var uploader = WebUploader.create({

    // swf文件路径

    swf: <%=basePath%>scripts/webuploader-0.1.5/Uploader.swf’,

    // 文件接收服务端。

    server: <%=basePath%>UploadVideo’,

    // 选择文件的按钮。可选。

    // 内部根据当前运行是创建,可能是input元素,也可能是flash.

    pick: {

      id: ‘#add_video’,

      //这个id是你要点击上传文件的id,自己设置就好

      multiple: false

    },

    // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!

    resize: true,

    auto: true,

    //开启分片上传

    chunked: true,

    chunkSize: 10 * 1024 * 1024,

    accept: {

      //限制上传文件为MP4

      extensions: ‘mp4’,

      mimeTypes: ‘video/mp4’,

    }

  });

  // 当有文件被添加进队列的时候

  uploader.on(‘fileQueued’,

  function(file) {

    $(‘#item1’).empty();

    $(‘#item1’).html(‘<div id=”‘ + file.id + ‘” class=”item”>’ + ‘<a class=”upbtn” id=”btn” onclick=”stop()”>[取消上传]</a>’ + ‘<p class=”info”>’ + file.name + ‘</p>’ + ‘<p class=”state”>等待上传…</p></div>’);

  });

  // 文件上传过程中创建进度条实时显示。

  uploader.on(‘uploadProgress’,

  function(file, percentage) {

    $(‘#item1’).find(‘p.state’).text(上传中 ‘ + Math.round(percentage * 100) + ‘%’);

  });

  uploader.on(‘uploadSuccess’,

  function(file) {

    $(‘#’ + file.id).find(‘p.state’).text(已上传’);

  });

  uploader.on(‘uploadError’,

  function(file) {

    $(‘#’ + file.id).find(‘p.state’).text(上传出错’);

  });

  uploader.on(‘uploadComplete’,

  function(file) {

    $(‘#’ + file.id).find(‘.progress’).fadeOut();

  });

  function start() {

    uploader.upload();

    $(‘#btn’).attr(“onclick”“stop()”);

    $(‘#btn’).text(取消上传”);

  }

  function stop() {

    uploader.stop(true);

    $(‘#btn’).attr(“onclick”“start()”);

    $(‘#btn’).text(继续上传”);

  }</script>//这个id是你要点击上传文件的id,自己设置就好 multiple:false}, // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传! resize: true, auto:true, //开启分片上传 chunked: true, chunkSize:10*1024*1024, accept: { //限制上传文件为MP4 extensions: ‘mp4’, mimeTypes: ‘video/mp4’, } }); // 当有文件被添加进队列的时候 uploader.on( ‘fileQueued’, function( file ) { $(‘#item1’).empty(); $(‘#item1’).html(‘

<divid=“‘ + file.id + ‘”class=“item”>‘+ ‘

  <aclass=“upbtn”id=“btn”onclick=“stop()”>[取消上传]</a>‘+ ‘

  <pclass=“info”>‘ + file.name + ‘</p>‘ + ‘

  <pclass=“state”>等待上传…</p></div>‘ ); }); // 文件上传过程中创建进度条实时显示。 uploader.on( ‘uploadProgress’, function( file, percentage ) { $(‘#item1’).find(‘p.state’).text(‘上传中 ‘+Math.round(percentage * 100) + ‘%’); }); uploader.on( ‘uploadSuccess’, function( file ) { $( ‘#’+file.id ).find(‘p.state’).text(‘已上传’); }); uploader.on( ‘uploadError’, function( file ) { $( ‘#’+file.id ).find(‘p.state’).text(‘上传出错’); }); uploader.on( ‘uploadComplete’, function( file ) { $( ‘#’+file.id ).find(‘.progress’).fadeOut(); }); function start(){ uploader.upload(); $(‘#btn’).attr(“onclick”,”stop()”); $(‘#btn’).text(“取消上传”); } function stop(){ uploader.stop(true); $(‘#btn’).attr(“onclick”,”start()”); $(‘#btn’).text(“继续上传”); }</script>

 

2、servlet部分代码:

servlet部分需要两个servlet,一个用于接收分块文件,一个用于合并分块成一个文件:

1、接收分块servlet代码:

@SuppressWarnings(“serial”)publicclass UploadVideo extends HttpServlet {@Override protectedvoid doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException,

    IOException {

        // TODO Auto-generated method stub

        super.doGet(req, resp);

        doPost(req, resp);

    }@SuppressWarnings(“unchecked”)publicvoid doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException,

    IOException {

        DiskFileItemFactory factory =new DiskFileItemFactory();

        ServletFileUpload sfu =new ServletFileUpload(factory);

        sfu.setHeaderEncoding(“utf-8”);

        String savePath =this.getServletConfig().getServletContext().getRealPath(“”);

        String folad =“uploads”;

        savePath = savePath +“\\”+ folad +“\\”;

        String fileMd5 =null;

        String chunk =null;

        try{

            List < FileItem > items = sfu.parseRequest(request);

            for(FileItem item: items){

                if(item.isFormField()){

                    String fieldName = item.getFieldName();

                    if(fieldName.equals(“fileMd5”)){

                        fileMd5 = item.getString(“utf-8”);

                    }

                    if(fieldName.equals(“chunk”)){

                        chunk = item.getString(“utf-8”);

                    }

                }else{

                    File file =new File(savePath +“/”+ fileMd5);

                    if(!file.exists()){

                        file.mkdir();

                    }

                    File chunkFile =new File(savePath +“/”+ fileMd5 +“/”+ chunk);

                    FileUtils.copyInputStreamToFile(item.getInputStream(), chunkFile);

                }

            }

        }catch(FileUploadException e){

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

}

 

2、合并分块servlet代码:

@SuppressWarnings(“serial”)publicclass Video extends HttpServlet {

    publicvoid doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException,

    IOException {

        super.doGet(request, response);

        doPost(request, response);

    }

    publicvoid doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException,

    IOException {

        String savePath =this.getServletConfig().getServletContext().getRealPath(“”);

        String folad =“uploads”;

        savePath = savePath +“\\”+ folad +“\\”;

        String action = request.getParameter(“action”);

        if(action.equals(“mergeChunks”)){

            //合并文件

            //需要合并的文件的目录标记

            String fileMd5 = request.getParameter(“fileMd5”);

            //读取目录里的所有文件

            File f =new File(savePath +“/”+ fileMd5);

            File[] fileArray = f.listFiles(new FileFilter(){

                //排除目录只要文件

                @Override publicboolean accept(File pathname){

                    // TODO Auto-generated method stub

                    if(pathname.isDirectory()){

                        returnfalse;

                    }

                    returntrue;

                }

            });

            //转成集合,便于排序

            List < File > fileList =new ArrayList < File >(Arrays.asList(fileArray));

            Collections.sort(fileList,new Comparator < File >(){@Override publicint compare(File o1, File o2){

                    // TODO Auto-generated method stub

                    if(Integer.parseInt(o1.getName())< Integer.parseInt(o2.getName())){

                        return 1;

                    }

                    return 1;

                }

            });

            //UUID.randomUUID().toString()–>随机名

            File outputFile =new File(savePath +“/”+ fileMd5 +“.mp4”);

            //创建文件

            outputFile.createNewFile();

            //输出流

            FileChannel outChnnel =new FileOutputStream(outputFile).getChannel();

            //合并

            FileChannel inChannel;

            for(File file: fileList){

                inChannel =new FileInputStream(file).getChannel();

                inChannel.transferTo(0, inChannel.size(), outChnnel);

                inChannel.close();

                //删除分片

                file.delete();

            }

            outChnnel.close();

            //清除文件夹

            File tempFile =new File(savePath +“/”+ fileMd5);

            if(tempFile.isDirectory()&& tempFile.exists()){

                tempFile.delete();

            }

            System.out.println(合并成功”);

        }elseif(action.equals(“checkChunk”)){

            //检查当前分块是否上传成功

            String fileMd5 = request.getParameter(“fileMd5”);

            String chunk = request.getParameter(“chunk”);

            String chunkSize = request.getParameter(“chunkSize”);

            File checkFile =new File(savePath +“/”+ fileMd5 +“/”+ chunk);

            response.setContentType(“text/html;charset=utf-8”);

            //检查文件是否存在,且大小是否一致

            if(checkFile.exists()&& checkFile.length()== Integer.parseInt(chunkSize)){

                //上传过

                response.getWriter().write(“{\”ifExist\”:1}”);

            }else{

                //没有上传过

                response.getWriter().write(“{\”ifExist\”:0}”);

            }

        }

    }

}

 

至此,大文件上传的分块和断点就ok了,这也只是我自己的项目需求编写的,这个框架还涵盖很多的内容和功能,需要你自己去研究了,不过都不是很难,你也可以去修改它的css和js文件根据自己的需求。

详细的配置信息可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/06/14/java%e6%89%b9%e9%87%8f%e4%b8%8b%e8%bd%bd%e6%96%87%e4%bb%b6%e5%88%b0%e6%8c%87%e5%ae%9a%e6%96%87%e4%bb%b6%e5%a4%b9/