接着上一篇,实现多个大文件的断点续传。

前端JSP页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html style="height: 99%; width: 99.9%">
    <head>
        <title>文件上传</title>
        <link rel="stylesheet" href="${ctx}/js/webuploader-0.1.5/webuploader.css" />
        <script src="${ctx}/js/jquery-ui-1.11.1/jquery-ui.min.js"></script>
        <script src="${ctx}/js/webuploader-0.1.5/webuploader.js"></script>        
    </head>
    <body class="div-form-container">
        <table class="upload">
            <tbody>
                <tr>
                    <td height="200px">
                        <div id="uploader-container" style="width: 100%">
                            <div id="fileList" class="uploader-list">
                            </div>
                            <div id="upInfo"></div>
                            <div id="filePicker">选择文件</div>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td>
                        <input type="button" class="btnSave" id="btnUpload" value="上传"/>
                        <input type="button" class="btnSave" id="btnReset" value="重置"/>
                        <input type="button" class="btnSave" value="取消" οnclick="javascript:closeDialog();"/>
                    </td>
                </tr>
            </tbody>
        </table>
        <script type="text/javascript">
        $(function() {
            var $chunkSize = 10*1024*1024, // 分片尺寸 10M
            $maxSingleSize = 1024*1024*1024, // 单文件最大尺寸
            $maxSize = 10*1024*1024*1024; // 所有文件最大尺寸
            var $list = $('#fileList'), // 页面展示的文件列表
            $fileArray = new Array(), // 要上传的文件列表
            $md5Array = new Array(), // 文件的MD5
            $nameArray = new Array(), // 文件名称
            count = 0, // 正在上传的文件在上传列表中的位置
            uploader; // Web Uploader实例
            
            // 监听分块上传的三个事件
            WebUploader.Uploader.register({
                "before-send-file" : "beforeSendFile", // 所有分块上传之前
                "before-send" : "beforeSend", // 每个分块上传之前
                "after-send-file" : "afterSendFile" // 所有分块上传完成后
            },{
                beforeSendFile : function(file){
                    var deferred = WebUploader.Deferred();
                    // 计算文件的MD5
                    (new WebUploader.Uploader()).md5File(file,0,10*1024*1024)
                    // 及时显示进度
                    .progress(function(percentage) {})
                    // 计算完成,继续下一步
                    .then(function(val) {
                        $md5Array.push(val);
                        $nameArray.push(file.name);
                        deferred.resolve();
                    });
                    return deferred.promise();
                },
                beforeSend : function(block){
                    var deferred = WebUploader.Deferred();
                    // 每个分块上传之前校验是否已上传
                    var url = "${ctx}/uploadBig/check",
                    param = {
                        fileName : $nameArray[count],
                        fileMd5 : $md5Array[count],
                        chunk : block.chunk,
                        chunkSize : block.end - block.start
                    };
                    // 同步校验,防止没校验完就上传了
                    $.ajaxSetup({async : false});
                    $.post(url,param,function(data){
                        // 已上传则跳过,否则继续上传
                        if(1 == data){
                            deferred.reject();
                        }else{
                            deferred.resolve();
                        }
                    });
                    $.ajaxSetup({async : true});
                    this.owner.options.formData.fileMd5 = $md5Array[count];
                    deferred.resolve();
                    return deferred.promise();
                },
                afterSendFile : function(){
                    // 所有分块上传完毕,通知后台合并分块
                    var url = "${ctx}/uploadBig/merge",
                    // 上传前设置其它参数
                    param = {
                        fileMd5 : $md5Array[count],
                        fileName : $nameArray[count]
                    };
                    $.ajaxSetup({async : false});
                    $.post(url,param,function(data){
                        count++;
                        if(count<=$fileArray.length-1){
                            uploader.upload($fileArray[count].id);
                        }
                    });
                    $.ajaxSetup({async : true});
                }
            });
            
            // 初始化Web Uploader PS:IE使用的flash上传,真心慢,大文件还是用Chrome上传比较靠谱
            uploader = WebUploader.create({
                auto : false, // 手动上传
                swf : '${ctx}/js/webuploader-0.1.5/Uploader.swf',
                server : '${ctx}/uploadBig/upload', // 文件接收服务端
                threads : 1, // 只运行1个线程传输
                duplicate : false, // 是否重复上传(单次选择同样的文件)
                prepareNextFile : true, // 允许在文件传输时提前把下一个文件准备好
                chunked : true, // 是否要分片处理大文件上传
                chunkSize : $chunkSize, // 如果要分片,分多大一片? 10M默认大小为5M
                fileNumLimit : 10, // 文件总数量 
                fileSingleSizeLimit : $maxSingleSize, // 单个文件大小限制
                fileSizeLimit : $maxSize, // 文件总大小限制
                pick : {
                    id : '#filePicker', // 选择文件的按钮
                    multiple : true // 允许同时选择多个文件
                },
                compress: false, // 不压缩文件
                accept : {
                    // TODO:待确认上传文件的格式和大小
                    // 常见视频文件格式:avi,wmv,mpeg,mp4,mov,mkv,flv,f4v,m4v,rmvb,rm,3gp,dat,ts,mts,vob
                    extensions: "txt,gif,jpg,jpeg,bmp,png,zip,rar,war,pdf,cebx,doc,docx,ppt,pptx,xls,xlsx",
                    mimeTypes: '.txt,.gif,.jpg,.jpeg,.bmp,.png,.zip,.rar,.war,.pdf,.cebx,.doc,.docx,.ppt,.pptx,.xls,.xlsx',
                }
            });
 
            // 当有文件添加进来的时候 
            uploader.on('fileQueued', function(file) {
                if((file.size <= $chunkSize) || (file.size > $maxSingleSize)){
                    return;
                }
                var $li = $('<div id="' + file.id + '" class="file-item">'
                        + '<span class="info">' + file.name + '</span>'
                        + '<span class="state">等待上传</span>'
                        + '</div>');
                $list.append($li);
                $fileArray.push(file);
            });
            
            // 对于太小的文件进行提示
            uploader.on('filesQueued',function (files){
                var smallFiles = '';
                for(i=0;i<files.length;i++){
                    var name = files[i].name,
                    size = files[i].size;
                    if(size <= $chunkSize){
                        smallFiles += name + ','
                    }
                }
                var msg = '';
                if(''!=smallFiles){
                    msg += "文件" + smallFiles + "小于10M,";
                }
                
                if('' != msg){
                    msg+="系统不支持上传这些文件!";
                    showTipsMsg(msg,3000,3);
                    return;
                }
            });
            
            // 上传中
            uploader.on('uploadProgress', function (file, percentage) {
                var $li = $('#' + file.id);
                var $state = $li.find('span.state');
                $state.html("<font color='#330033'>上传中...</font>");
            });
 
            // 文件上传成功
            uploader.on('uploadSuccess', function(file, response) {
                var $li = $('#' + file.id);
                var $state = $li.find('span.state');
                $state.html("<font color='green'>上传成功!</font>");
            });
 
            // 文件上传失败
            uploader.on('uploadError', function(file, code) {
                var $li = $('#' + file.id);
                var $state = $li.find('span.state');
                $state.html("<font color='red'>上传失败!</font>");
            });
 
            // 手动上传
            $("#btnUpload").click(function() {
                // 执行上传操作
                uploader.upload();
            });
            
            // 重置
            $("#btnReset").click(function() {
                // 清空文件列表,重置上传控件
                $list.html('');
                $fileArray = new Array();
                $md5Array = new Array();
                $nameArray = new Array();
                count = 0;
                uploader.reset();
            });
        });
    </script>
    </body>
</html>

后端Controller

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
 
import com.alibaba.fastjson.JSON;
 
 
@Controller
@RequestMapping(value = "/uploadBig")
public class BigFileUploadController extends BaseController {
    /** 日志记录 */
    private static Logger logger = Logger.getLogger(BigFileUploadController.class);
 
    /**
     * 保存文件分片
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     */
    @RequestMapping(value = "/upload",
        method = RequestMethod.POST)
    public void upload(HttpServletRequest request, HttpServletResponse response) {
        // 参数获取
        String fileName = request.getParameter("name");
        String fileMd5 = request.getParameter("fileMd5");
        String chunk = request.getParameter("chunk");
        SysUser user = getSysUser(request);
        // 临时目录
        String tempPath = FilePathUtil.getRootTempPath();
        // 转换请求对象得到文件对象
        MultipartHttpServletRequest Murequest = (MultipartHttpServletRequest) request;
        Map<String, MultipartFile> files = Murequest.getFileMap();
        if (null != files && !files.isEmpty()) {
            for (MultipartFile item : files.values()) {
                String tempDir = getTempDir(tempPath, user.getUserName(), fileName, fileMd5);
                // 创建临时文件夹
                File dir = new File(tempDir);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                // 创建分片文件并保存
                File chunkFile = new File(tempDir + File.separator + chunk);
                try {
                    chunkFile.createNewFile();
                    item.transferTo(chunkFile);
                } catch (IllegalStateException e) {
                    logger.error("保存分片文件出错:" + e.getMessage());
                    e.printStackTrace();
                } catch (IOException e) {
                    logger.error("保存分片文件出错:" + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }
 
    /**
     * 校验分片是否已上传
     * 
     * @Title: checkChunk
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     */
    @RequestMapping(value = "/check",
        method = RequestMethod.POST)
    public void checkChunk(HttpServletRequest request, HttpServletResponse response) {
        // 获取参数
        String fileName = request.getParameter("fileName");
        String fileMd5 = request.getParameter("fileMd5");
        String chunk = request.getParameter("chunk");
        String chunkSize = request.getParameter("chunkSize");
        SysUser user = getSysUser(request);
        // 临时文件
        String tempPath = FilePathUtil.getRootTempPath();
        String tempDir = getTempDir(tempPath, user.getUserName(), fileName, fileMd5);
        File chunkFile = new File(tempDir + File.separator + chunk);
        int result = 0;
        // 分片文件是否存在,尺寸是否一致
        if (chunkFile.exists() && chunkFile.length() == Integer.parseInt(chunkSize)) {
            result = 1;
        }
        returnJSONData(response, JSON.toJSONString(result));
    }
 
    /**
     * 合并分片成完整文件
     * 
     * @Title: mergeChunks
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     */
    @SuppressWarnings("resource")
    @RequestMapping(value = "/merge",
        method = RequestMethod.POST)
    public void mergeChunks(HttpServletRequest request, HttpServletResponse response) {
        // 获取参数
        String fileName = request.getParameter("fileName");
        String fileMd5 = request.getParameter("fileMd5");
        SysUser user = getSysUser(request);
        // 获得目标文件夹
        String savePath = FilePathUtil.getFilePath();
        
        // 创建目标文件夹
        File saveDir = new File(savePath);
        if (!saveDir.exists()) {
            saveDir.mkdirs();
        }
 
        // 文件临时文件夹"根目录/temp/userName/文件名/MD5"
        String tempDirPath = getTempDir(FilePathUtil.getRootTempPath(), user.getUserName(), fileName, fileMd5);
        File tempDir = new File(tempDirPath);
        // 获得分片文件列表
        File[] fileArray = tempDir.listFiles(new FileFilter() {
            // 只需要文件
            @Override
            public boolean accept(File pathname) {
                if (pathname.isDirectory()) {
                    return false;
                } else {
                    return true;
                }
            }
        });
        // 转成集合进行排序后合并文件
        List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));
        Collections.sort(fileList, new Comparator<File>() {
            // 按文件名升序排列
            @Override
            public int compare(File o1, File o2) {
                if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
                    return -1;
                } else {
                    return 1;
                }
            }
        });
 
        // 目标文件
        File outfile = new File(savePath + File.separator + fileName);
        try {
            outfile.createNewFile();
        } catch (IOException e) {
            logger.error("创建目标文件出错:" + e.getMessage());
            e.printStackTrace();
        }
 
        // 执行合并操作
        FileChannel outChannel = null;
        FileChannel inChannel;
        try {
            outChannel = new FileOutputStream(outfile).getChannel();
            for (File file : fileList) {
                inChannel = new FileInputStream(file).getChannel();
                inChannel.transferTo(0, inChannel.size(), outChannel);
                inChannel.close();
                file.delete();
            }
            outChannel.close();
        } catch (FileNotFoundException e) {
            logger.error("合并分片文件出错:" + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            logger.error("合并分片文件出错:" + e.getMessage());
            e.printStackTrace();
        }
 
        // 删除临时文件夹 根目录/temp/userName/fileName
        File tempFileDir = new File(
            FilePathUtil.getRootTempPath() + File.separator + user.getUserName() + File.separator + fileName);
        FileUtil.deleteDir(tempFileDir);
    }
 
    /**
     * 获得分片文件临时保存路径
     * 
     * @Title: getTempDir
     * @param tempPath 临时文件根目录
     * @param userName 用户名
     * @param fileName 文件名
     * @param fileMd5 文件MD5
     * @return String 分片临时保存路径
     */
    private String getTempDir(String tempPath, String userName, String fileName, String fileMd5) {
        StringBuilder dir = new StringBuilder(tempPath);
        dir.append(File.separator).append(userName);
        dir.append(File.separator).append(fileName);
        dir.append(File.separator).append(fileMd5);
        return dir.toString();
    }
}

[整理]WebUploader + SpringMVC 实现多文件断点续传之二 多文件断点续传相关推荐

  1. Python -- 文件和异常(二) — 写入文件

    写入文件 保存数据的最简单的方式之一是将其写入到文件中.通过将输出写入文件,即便关闭包含程序输出的终端窗口,这些输出也依然存在:你可以在程序结束运行后查看这些输出,可与别人分享输出文件,还可编写程序来 ...

  2. ansible笔记(5):常用模块之文件操作(二)

    ansible笔记(5):常用模块之文件操作(二)文件操作类模块find模块 find模块可以帮助我们在远程主机中查找符合条件的文件,就像find命令一样.此处我们介绍一些find模块的常用参数,你可 ...

  3. 文件包含原理及本地文件包含漏洞演示(本地文件,远程包含文件的测试)

    一.文件包含漏洞概述 1.定义:文件包含漏洞是一种最常见的漏洞类型,它会影响依赖于脚本运行时的web应用程序.当应用程序使用攻击者控制的变量构建可执行代码的路径时,文件包含漏洞会导致攻击者任意控制运行 ...

  4. html怎么查看cad文件,怎么可以查看CAD文件信息和文件版本?

    步骤一:任意打开一张CAD图纸 运行迅捷CAD编辑器专 业版里,点 击菜单栏"文件-打开"选项按钮,任意打开一张需要查看图形特性的CAD图纸文件. 步骤二:文件-图形特性 1.点 ...

  5. 【文件I/O】(二)文件I/O

    文件I/O:系统调用 一.文件I/O基本概念 1.什么是文件I/O? 2.文件描述符 二.文件I/O函数 head.h 1.open.close(打开.关闭文件) 1.1open.close函数API ...

  6. java web 断点上传_使用WebUploader实现分片断点上传文件功能(二)

    写在前面: 这几天,有去研究一下WebUploader上传文件,前面的博客有记录下使用WebUploader简单上传文件的例子,今天就把分片断点上传的例子也记录下吧,在博客园中,也查看了一些资料,基本 ...

  7. 高薪程序员面试题精讲系列23之说一下如何实现文件上传、下载以及断点续传?

    一. 面试题及剖析 1. 今日面试题 在上一篇文章中,壹哥 给大家总结了I/O流相关的内容,复习了各种I/O流相关API.其实我们去面试的时候,面试官提问的前后两个题目之间往往会具有一定的关联性.比如 ...

  8. Android下载文件(一)下载进度断点续传

    Android下载文件(一)下载进度&断点续传 索引 Android下载文件(一)下载进度&断点续传 Android下载文件(二)单任务多线程并发&断点续传(待续) Andro ...

  9. php保存成乱序,PHP实现断点续传乱序合并文件的方法

    本文实例讲述了PHP实现断点续传乱序合并文件的方法.分享给大家供大家参考,具体如下: 分割成多个文件发送,由于网络原因并不上先发就能发接收到.所以我们不能按顺序合并. 分割文件源码前面一篇文章< ...

  10. 前端接收pdf文件_如何实现springmvc将返回的给前端的pdf文件放在浏览器里预览

    展开全部 1,在web路径下建立一个uploadFiles文件636f707962616964757a686964616f31333361316561夹. 2,在springMVC里映射PDF文件就像 ...

最新文章

  1. C++编写DLL的方法
  2. 矩阵论-线性空间的基与坐标,基变换坐标变换
  3. MSSQL调优实战一 乱建聚集索引的后果
  4. golang 判断map的键key是否存在
  5. es6标准入门(第三版)_阮一峰 pdf
  6. Java代码审查工具 FindBugs下载、安装和使用(无需集成环境一键安装使用)
  7. mysql导入人员信息_mysql中导入数据库
  8. 无源贴片晶振四角引脚_如何区分贴片晶振的脚位方向
  9. 系统——现有centos7操作系统制作为iso镜像文件
  10. 报表工具——开源还是商用
  11. 读just spring(一)(翻译的不好,都是用自己的话)
  12. HTML——表白树动画
  13. 带例子的测试用例模板
  14. 小白也能学会的调色教程,你学会了吗?
  15. 基于单片机交通灯控制的c语言程序设计,基于单片机控制的交通灯毕业设计
  16. Android Studio中layout_gravity与gravity
  17. 训练动作→肌肉记忆(Muscle Memory)
  18. 小而美的个人博客项目总结(mybatis)
  19. 2020cvpr-Correlation-Guided Attention for Corner Detection Based Visual Tracking
  20. 天融信防火墙配置“时间”的问题

热门文章

  1. 企业微信API全局错误码 enum枚举类
  2. latex---插入三线表伪代码流程图
  3. 手机游戏源码下载的网站
  4. 操作系统第四版习题答案大全
  5. 使用融云 SDK 避坑指南之 iOS13 推送失败
  6. iOS文本展开收起,使用YYKit展开全文和收起全文,支持图文混排
  7. 使用makefile生成.so文件
  8. Latex 带圈数字
  9. java基础下载音乐_抖音上超好听的神曲音乐,Python教你一次性下载
  10. 幼儿园门口摆个考勤机 家长接孩子得打卡