页面引用:

<!--引用tag标签路径,并定义为sys-->
<%@ taglib prefix="sys" tagdir="/WEB-INF/tags/sys" %>
 
<!--以下两个隐藏域为spring标签,可以用常规html代替,用以存放上传文件返回的文件真实地址和文件访问地址-->
<form:hidden id="hzzqtysUrl" path="hzzqtysUrl" htmlEscape="false" maxlength="255" class="input-xlarge required"/>
<form:hidden id="hzzqtysTitle" path="hzzqtysTitle"  htmlEscape="false" maxlength="255" class="input-xlarge"/>

<!--前端引用tag组件方法-->
<sys:webuploader input="hzzqtysUrl" label="hzzqtysTitle" type="all"/>
tag组件:webuploader.tag

<%@ tag language="java" pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<%@ attribute name="input" type="java.lang.String" required="true" description="文件路径用'|'分割组成"%>
<%@ attribute name="label" type="java.lang.String" required="true" description="文件名称用'|'分割组成"%>
<%@ attribute name="type" type="java.lang.String" required="true" description="images、docs、all"%>
<%@ attribute name="fileSingleSizeLimit" type="java.lang.String" required="false" description="单文件最大尺寸(kb)"%>
<%@ attribute name="fileSizeLimit" type="java.lang.String" required="false" description="文件总大小最大尺寸(kb)"%>
<%@ attribute name="chunkSize" type="java.lang.String" required="false" description="分片大小(kb),默认大小为5M"%>
<%@ attribute name="fileNumLimit" type="java.lang.Integer" required="false" description="文件总数量"%>
<%@ attribute name="readonly" type="java.lang.Boolean" required="false" description="是否查看模式"%>
<%@ attribute name="maxWidth" type="java.lang.String" required="false" description="最大宽度"%>
<%@ attribute name="maxHeight" type="java.lang.String" required="false" description="最大高度"%>
<link href="${ctxStatic }/webuploader-0.1.5/webuploader.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="${ctxStatic }/webuploader-0.1.5/webuploader.js"></script>
 
<input id="${input}jindutiao" type="hidden" />
<input id="${input}fileIds" type="hidden" />
<div id="${input}uploader" class="wu-example">
    <div class="btns">
        <div id="${input}filePicker" class="webuploader-container">
            <div class="webuploader-pick">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上传&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
                <input id="${input}file_bp" name="file"
                       class="webuploader-element-invisible" type="file" />
        </div>
        <!-- 文件列表:选择文件后在该div显示 -->
        <ol id="${input}fileList"></ol>
        <label class="text-right"
               style="font-weight: 100; float: left; margin-left: 15px; width: 144px; margin-right: 15px;"></label>
    </div>
</div>
 
 
<script type="text/javascript">
    var preShowPath = $("#preShowPath").val();//访问文件前缀
    var ${input}_$md5Array = new Array(), // 文件的MD5
        ${input}_$nameArray = new Array(), // 文件名称
        ${input}_count = 0, // 正在上传的文件在上传列表中的位置
        ${input}_success=0,//上传成功的文件数
        ${input}_map={},//key存储文件id,value存储该文件上传过的进度
        ${input}_uploader; // Web Uploader实例
    var ${input}_webUploader = WebUploader;
   $(function() {
 
        // 监听分块上传的三个事件
        ${input}_webUploader.Uploader.register({
            "before-send-file" : "${input}_beforeSendFile", // 所有分块上传之前
            "before-send" : "${input}_beforeSend", // 每个分块上传之前
            "after-send-file" : "${input}_afterSendFile" // 所有分块上传完成后
        },{
            ${input}_beforeSendFile : function(file){
                var deferred = ${input}_webUploader.Deferred();
                // 计算文件的MD5
                ${input}_uploader.md5File(file,0,10*1024*1024)
                // 及时显示进度
                    .progress(function(percentage) {
                        // $('#'+file.id ).find('p.state').text('正在读取文件信息...');
                    })
                    // 计算完成,继续下一步
                    .then(function(val) {
                        // $('#'+file.id ).find("p.state").text("成功获取文件信息...");
                        ${input}_$md5Array.push(val);
                        ${input}_$nameArray.push(file.name);
                        deferred.resolve();
                    });
                return deferred.promise();
            },
            ${input}_beforeSend : function(block){
                var deferred = ${input}_webUploader.Deferred();
                // 每个分块上传之前校验是否已上传
                var url = "${ctx}/uploadBig/check",
                    param = {
                        fileName : ${input}_$nameArray[${input}_count],
                        fileMd5 : ${input}_$md5Array[${input}_count],
                        jindutiao:$("#${input}jindutiao").val(),
                        chunk : block.chunk,
                        chunkSize : block.end - block.start
                    };
                // 同步校验,防止没校验完就上传了
                $.ajaxSetup({async : false});
                $.post(url,param,function(data){
                    var res = eval('('+data+')');
                    // 已上传则跳过,否则继续上传
                    if(res.ifExist){
                        deferred.reject();
                    }else{
                        deferred.resolve();
                    }
                },"json");
                $.ajaxSetup({async : true});
                this.owner.options.formData.fileMd5 = ${input}_$md5Array[${input}_count];
                deferred.resolve();
                return deferred.promise();
            },
            ${input}_afterSendFile : function(file){
                // 所有分块上传完毕,通知后台合并分块
                var url = "${ctx}/uploadBig/merge",
                    // 上传前设置其它参数
                    param = {
                        fileMd5 : ${input}_$md5Array[${input}_count],
                        fileName : ${input}_$nameArray[${input}_count]
                    };
                $.ajaxSetup({async : false});
                $.post(url,param,function(data){
 
                    var urlFilePaths =data.fileUrl;
                    var urlFileNames = file.name;
                    var fileIds = file.id;
                    preShowPath = data.preShowPath;
 
                    //单选或多选模式
                    <c:if test="${fileNumLimit eq 1}">
                    $("#${input}").val(urlFilePaths);
                    $("#${label}").val(urlFileNames);
                    $("#${input}fileIds").val(fileIds);
                    </c:if>
 
                    <c:if test="${fileNumLimit ne 1}">
                    var origFilePaths = $("#${input}").val();
                    var origFileNames = $("#${label}").val();
                    var origFileIds = $("#${input}fileIds").val();
 
                    if(""==origFilePaths){
                        $("#${input}").val(urlFilePaths);
                    }else{
                        $("#${input}").val(origFilePaths + "|"+ urlFilePaths);
                    }
 
                    if(""==origFileNames){
                        $("#${label}").val(urlFileNames);
                    }else{
                        $("#${label}").val(origFileNames + "|"+ urlFileNames);
                    }
 
                    if(""==origFileIds){
                        $("#${input}fileIds").val(fileIds);
                    }else{
                        $("#${input}fileIds").val(origFileIds + "|"+ fileIds);
                    }
                    </c:if>
 
                    ${input}Preview(${input}_map[file.id]);
 
                    ${input}_count++;
 
                    ${input}_uploader.upload(file);
 
                },"json");
                $.ajaxSetup({async : true});
            }
        });
 
        // 初始化Web Uploader PS:IE使用的flash上传,真心慢,大文件还是用Chrome上传比较靠谱
        ${input}_uploader = ${input}_webUploader.create({
            auto : true, // 自动上传
            swf : '${ctxStatic}/webuploader-0.1.5/Uploader.swf',
            server : '${ctx}/uploadBig/upload', // 文件接收服务端
            threads : 1, // 只运行1个线程传输
            duplicate : false, // 是否重复上传(单次选择同样的文件)
            prepareNextFile : true, // 允许在文件传输时提前把下一个文件准备好
            chunked : true, // 是否要分片处理大文件上传
            <c:if test="${not empty chunkSize}">
            chunkSize : ${chunkSize}, // 如果要分片,分多大一片? 10M默认大小为5M
            </c:if>
            fileNumLimit : ${empty fileNumLimit? 10:fileNumLimit}, // 文件总数量
            <c:if test="${not empty fileSingleSizeLimit}">
            fileSingleSizeLimit : ${fileSingleSizeLimit}, // 单个文件大小限制
            </c:if>
            <c:if test="${not empty fileSizeLimit}">
            fileSizeLimit : ${fileSizeLimit}, // 文件总大小限制
            </c:if>
            pick : {
                id : '#${input}filePicker', // 选择文件的按钮
                multiple : false // 是否允许同时选择多个文件
            },
            compress: false, // 不压缩文件
            <c:if test="${type eq 'images'}">
            accept : {
                extensions: "gif,jpg,jpeg,bmp,png",
                mimeTypes: '.gif,.jpg,.jpeg,.bmp,.png',
            }
            </c:if>
            <c:if test="${type eq 'docs'}">
            accept : {
                extensions: "txt,pdf,cebx,doc,docx,ppt,pptx,xls,xlsx",
                mimeTypes: '.txt,.pdf,.cebx,.doc,.docx,.ppt,.pptx,.xls,.xlsx',
            }
            </c:if>
            <c:if test="${type eq 'all'}">
            accept : {
                // 常见视频文件格式: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',
            }
            </c:if>
        });
 
        // 当有文件添加进来的时候
        ${input}_uploader.on('fileQueued', function(file) {
            //进度条显示
            $("#mask").css("height",$(document).height());
            $("#mask").css("width",$(document).width());
            $("#mask").show();
 
            ${input}_success++;
 
            $.ajax({
                type:"POST",
                url:"${ctx}/uploadBig/selectProgressByFileName",  //先检查该文件是否上传过,如果上传过,上传进度是多少
                data:{
                    fileMD5 : ${input}_$md5Array[${input}_count]  //文件MD5
                },
                cache: false,
                async: false,  // 同步
                //dataType:"json",
                success:function(result){
                    var res = eval('('+result+')');
 
                    //上传过
                    if(res.jindutiao>0){
                        //将上传过的进度存入map集合
                        ${input}_map[file.id]=res.jindutiao/100;
                    }
                    $("#${input}filePicker").append('<div id="${input}'+file.id+'1"></div>');
                }
            });
 
            ${input}_uploader.stop(true);
        });
 
        // 上传中
        ${input}_uploader.on('uploadProgress', function (file, percentage) {
            var $li = $( '#${input}'+file.id+'1' ),
                $percent = $li.find('.progress .progress-bar');
            //避免重复创建
            if (!$percent.length){
                $percent = $('<div class="progress progress-striped active">' +
                    '<div class="progress-bar" role="progressbar" style="width: 0%;height:20px; background-color:#2fa4e7;">' +
                    '</div>' +
                    '</div>').appendTo( $li ).find('.progress-bar');
            }
 
            //将实时进度存入隐藏域
            $("#${input}jindutiao").val(Math.round(percentage * 100));
 
            //根据fielId获得当前要上传的文件的进度
            var oldJinduValue = ${input}_map[file.id];
 
            if(percentage<oldJinduValue && oldJinduValue!=1){
                // $li.find('p.state').text('上传中'+Math.round(oldJinduValue * 100) + '%');
                $percent.text(Math.round(oldJinduValue * 100) + '%');
                $percent.css('width', oldJinduValue * 100 + '%');
            }else{
                // $li.find('p.state').text('上传中'+Math.round(percentage * 100) + '%');
                $percent.text(Math.round(percentage * 100) + '%');
                $percent.css('width', percentage * 100 + '%');
            }
        });
 
        // 文件上传成功
        ${input}_uploader.on('uploadSuccess', function(file) {
            //上传成功去掉进度条
            $('#${input}'+file.id+'1').find('.progress').fadeOut();
            // $('#'+file.id).find('p.state').text('');
            //隐藏进度条
            $("#mask").hide();
        });
 
        // 文件上传失败
        ${input}_uploader.on('uploadError', function(file) {
            ${input}_uploader.stop(true);
            // $('#'+file.id).find('p.state').text('上传出错,请检查网络连接');
            $.jBox.tip('上传出错,请检查网络连接');
            //隐藏进度条
            $("#mask").hide();
        });
        /**
         * 验证文件格式以及文件大小
         */
        ${input}_uploader.on("error",function (type){
            if (type=="Q_TYPE_DENIED"){
                $.jBox.tip('请上传正确格式的文件');
            }else if(type=="F_EXCEED_SIZE"){
                $.jBox.tip('请上传正确大小的文件');
            }
        });
   });
 
    function ${input}Del(obj){
        var newFilePaths = "";
        var newFileNames = "";
        var newFileIds = "";
        var url = $(obj).prev().attr("url");
        var origFilePaths = $("#${input}").val().split("|");
        var origFileNames = $("#${label}").val().split("|");
        var origFileIds = $("#${input}fileIds").val().split("|");
        if(origFilePaths.length == origFileNames.length){
            for (var i=0; i<origFilePaths.length; i++){
                if (preShowPath+origFilePaths[i]!=url){
                    if(""==newFilePaths){
                        newFilePaths = origFilePaths[i];
                    }else{
                        newFilePaths +="|"+origFilePaths[i];
                    }
 
                    if(""==newFileNames){
                        newFileNames = origFileNames[i];
                    }else{
                        newFileNames +="|"+origFileNames[i];
                    }
 
                    if(""==newFileIds){
                        newFileIds = origFileIds[i];
                    }else{
                        newFileIds +="|"+origFileIds[i];
                    }
                }
            }
        }else{
            $.jBox.alert('数据初始化出现异常: 数组长度不一致', '失败');
        }
        $("#${input}").val(newFilePaths);
        $("#${label}").val(newFileNames);
        $("#${input}fileIds").val(newFileIds);
 
        try{
            var fileItem = $(obj).parent();
            ${input}_uploader.removeFile($(fileItem).attr("id"), true);
        }catch(err){
            console.log('修改页面默认因为只是显示图片,删除操作可能报错,不影响整体功能');
        }
 
        ${input}Preview(0);
    }
 
    function ${input}Preview(bar){
        var tmp, urlFileNames = $("#${label}").val().split("|");
        var li, fileId, urls = $("#${input}").val().split("|")
            ,fileIds = $("#${input}fileIds").val().split("|");
 
        $("#${input}fileList").children().remove();
        //上传过
        if(bar>0){
            //如果上传过 上传了多少
            var jindutiaoStyle="width:"+bar+"%";
            for (var i=0; i<urls.length; i++){
                if (urls[i]!=""){
                    tmp = urls[i].substring(urls[i].lastIndexOf("/")+1);
                    if(typeof(fileIds[i])=='undefined'){
                        fileId = "WU_FILE_"+i;
                    }else{
                        fileId = fileIds[i];
                    }
                    if(urlFileNames.length==urls.length){
                        tmp = urlFileNames[i];
                    }
                    li = '<li id="'+fileId+'"><a href="'+preShowPath+urls[i]+'?filename='+decodeURIComponent(tmp)+'" url="'+preShowPath+urls[i]+'" target="_blank">';
                    <c:choose>
                    <c:when test="${type eq 'images'}">
                    li += '<img src="'+preShowPath+urls[i]+'" url="'+preShowPath+urls[i]+'" style="max-width:${empty maxWidth ? 200 : maxWidth}px;max-height:${empty maxHeight ? 200 : maxHeight}px;_height:${empty maxHeight ? 200 : maxHeight}px;border:0;padding:3px;">';
                    </c:when>
                    <c:otherwise>
                    li += decodeURIComponent(tmp);
                    </c:otherwise>
                    </c:choose>
 
                    li += '</a>&nbsp;&nbsp;<c:if test="${!readonly}"><a href="javascript:" οnclick="${input}Del(this);">×</a></c:if></li>'+
                        '<div class="progress progress-striped active">' +
                        '<div class="progress-bar" role="progressbar" style="'+jindutiaoStyle+'">';
                    $("#${input}fileList").append(li);
                }
            }
        }else{
            for (var i=0; i<urls.length; i++){
                if (urls[i]!=""){
                    tmp = urls[i].substring(urls[i].lastIndexOf("/")+1);
                    if(typeof(fileIds[i])=='undefined'){
                        fileId = "WU_FILE_"+i;
                    }else{
                        fileId = fileIds[i];
                    }
                    if(urlFileNames.length==urls.length){
                        tmp = urlFileNames[i];
                    }
 
                    li = '<li id="'+fileId+'"><a href="'+preShowPath+urls[i]+'?filename='+decodeURIComponent(tmp)+'" url="'+preShowPath+urls[i]+'" target="_blank">';
                    <c:choose>
                    <c:when test="${type eq 'images'}">
                    li += '<img src="'+preShowPath+urls[i]+'" url="'+preShowPath+urls[i]+'" style="max-width:${empty maxWidth ? 200 : maxWidth}px;max-height:${empty maxHeight ? 200 : maxHeight}px;_height:${empty maxHeight ? 200 : maxHeight}px;border:0;padding:3px;">';
                    </c:when>
                    <c:otherwise>
                    li += decodeURIComponent(tmp);
                    </c:otherwise>
                    </c:choose>
 
                    li += "</a>&nbsp;&nbsp;<c:if test="${!readonly}"><a href=\"javascript:\" οnclick=\"${input}Del(this);\">×</a></c:if></li>";
                    $("#${input}fileList").append(li);
                }
            }
        }
    }
 
    ${input}Preview(0);
</script>

java后台代码:BigFileUploadController.java

其中包含上传到FastDFS的方法(如果不需要可以把这部分代码去掉),感觉不太好,因为是先上传到服务器中的临时路径,然后,再用FastDFS上传的,没研究出来分片之后,直接上传到FastDFS服务器中的方法,希望有大神指点。

继承的BaseController类,只是用了里面的记录日志方法和一些url前缀取值,可以忽略。

还有一些Util类,以下只提供FileUtils类,其他Util类比较普遍,项目中应该都会有。

package com.senyint.ma.common.web;
 
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.common.servlet.entity.UploadNetStreamCallback;
import com.common.servlet.entity.UploadifyResult;
import com.common.utils.*;
import com.modules.sys.utils.UserUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.jasig.cas.client.util.CommonUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.*;
 
@Controller
@RequestMapping(value = "${adminPath}/uploadBig")
public class BigFileUploadController extends BaseController {
 
   /** Fastdfs configure file **/
   public static final String FASTDFS_CONIFG_FILE = "deploy.properties";
 
   private static final String FILE_UPLOAD = "upload";
 
   /** show files path **///e.g.  http://10.66.90.176:8888
   public static String SHOW_PATH = new PropertiesLoader("deploy.properties").getProperty("show_path");
 
   /** upload files path **/e.g.  windows: d:\\testUpload  linux: /temp
   public static String UPLOAD_PATH = new PropertiesLoader("deploy.properties").getProperty("noFdfs_upload_path");
 
   /** 上传标识 1.fastdfs 2.ftp 3.aliyun**/
   public static String UPLOAD_FLAG = new PropertiesLoader("deploy.properties").getProperty("uploadFlag");
 
   /** Fastdfs configure file **/
   protected static String SYS_CLASSES_PATH_FILE = "";
 
   /** Temporary Path **/
   protected static String SYS_TMP_PATH = "/tmp/";
 
   protected static String SYS_FASTDFS_TRACKER_HTTP_PORT="";
 
   /**
    * 保存文件分片
    * @param request HttpServletRequest
    * @param response HttpServletResponse
    */
   @RequestMapping(value = "/upload")
   @ResponseBody
   public String upload(HttpServletRequest request, HttpServletResponse response) {
      // 参数获取
      String fileMd5 = request.getParameter("fileMd5");
      String chunk = request.getParameter("chunk");
      // 临时目录
      String tempPath = request.getSession().getServletContext().getRealPath("/");//"D:\\testUpload\\upload\\";
      // 转换请求对象得到文件对象
      MultipartHttpServletRequest Murequest = (MultipartHttpServletRequest) request;
      Map<String, MultipartFile> files = Murequest.getFileMap();
      if (null != files && !files.isEmpty()) {
         for (MultipartFile item : files.values()) {
            String tempDir = getTempDir(tempPath, 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();
            }
         }
      }
      return "1";
   }
 
   /**
    * 校验分片是否已上传
    *
    * @Title: checkChunk
    * @param request HttpServletRequest
    * @param response HttpServletResponse
    */
   @RequestMapping(value = "/check",
         method = RequestMethod.POST)
   public void checkChunk(HttpServletRequest request, HttpServletResponse response) {
      // 获取参数
      String fileMd5 = request.getParameter("fileMd5");
      String chunk = request.getParameter("chunk");
      String chunkSize = request.getParameter("chunkSize");
      String jindutiao = request.getParameter("jindutiao");// 文件上传的实时进度
      // 临时文件
      String tempPath = request.getSession().getServletContext().getRealPath("/");//"D:\\testUpload\\upload\\";
      String tempDir = getTempDir(tempPath, fileMd5);
      File chunkFile = new File(tempDir + File.separator + chunk);
      // 将当前进度存入缓存
      CacheUtils.put(FILE_UPLOAD, "jindutiao_" + fileMd5, jindutiao);
//    System.out.println("缓存中的进度条="+jindutiao);
      // 分片文件是否存在,尺寸是否一致
      if (chunkFile.exists() && chunkFile.length() == Integer.parseInt(chunkSize)) {
         writeJSON(response,"{\"ifExist\":1}");
      }else{
         writeJSON(response,"{\"ifExist\":0}");
      }
   }
 
   /**
    * 合并分片成完整文件
    *
    * @Title: mergeChunks
    * @param request HttpServletRequest
    * @param response HttpServletResponse
    */
   @SuppressWarnings("resource")
   @RequestMapping(value = "/merge",
         method = RequestMethod.POST)
   @ResponseBody
   public String mergeChunks(HttpServletRequest request, HttpServletResponse response) {
      Calendar calendar=Calendar.getInstance();
      String month = "";
      String day = "";
      SimpleDateFormat sdfM = new SimpleDateFormat("MM");
      SimpleDateFormat sdfD = new SimpleDateFormat("dd");
      Date ddM  = Calendar.getInstance().getTime();
      Date ddD  = Calendar.getInstance().getTime();
      month = sdfM.format(ddM);
      day = sdfD.format(ddD);
      String filePath = UPLOAD_PATH + File.separatorChar+calendar.get(Calendar.YEAR)+month+day;//文件存放服务器路径
      String filePathUrl = "/"+calendar.get(Calendar.YEAR)+month+day;//文件访问路径
 
      // 获取参数
      String fileName = request.getParameter("fileName");
      String fileMd5 = request.getParameter("fileMd5");
      // 获得目标文件夹
      String tempPath = request.getSession().getServletContext().getRealPath("/");//"D:\\testUpload\\upload\\";
      // 创建目标文件夹
      File saveDir = new File(filePath);
      if (!saveDir.exists()) {
         saveDir.mkdirs();
      }
 
      // 文件临时文件夹"根目录/temp/userName/文件名/MD5"
      String tempDirPath = getTempDir(tempPath, fileMd5);
      File tempDir = new File(tempDirPath);
      // 获得分片文件列表
      File[] fileArray = tempDir.listFiles(new FileFilter() {
         // 只需要文件
         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>() {
         // 按文件名升序排列
         public int compare(File o1, File o2) {
            if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
               return -1;
            } else {
               return 1;
            }
         }
      });
      String extFileName = FileUtils.getFileExtension(fileName);
      String uuid = IdGen.uuid();
      // 目标文件
      File outfile = new File(filePath + File.separator + uuid + "." + extFileName);
      try {
         outfile.createNewFile();
      } catch (IOException e) {
         logger.error("创建目标文件出错:" + e);
      }
      // 执行合并操作
      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);
      } catch (IOException e) {
         logger.error("合并分片文件出错:读写错误" + e);
      }
      String fileId = filePath + File.separator + outfile.getName();
      String fileUrl = filePathUrl + "/" + outfile.getName();
      if(UPLOAD_FLAG.equals("fastdfs")){
         fileUrl = "";
         try {
            //初始化FastDFS 开始
            // Classes Path
            File f = new File(this.getClass().getResource("/").getPath());
            String classesPath = f.getAbsolutePath();
            SYS_CLASSES_PATH_FILE = classesPath + File.separator + FASTDFS_CONIFG_FILE;
            // Temporary Path
            String tmpDir = System.getProperty("java.io.tmpdir");
            if (null != tmpDir && tmpDir.trim().length() > 0) {
               SYS_TMP_PATH = tmpDir.trim();
            }
            // Init FastDFS
            ClientGlobal.init(SYS_CLASSES_PATH_FILE);
            // Tracker server host name
//          InetSocketAddress[] isa =ClientGlobal.g_tracker_group.tracker_servers;
   //          SYS_FASTDFS_HOST_NAME = isa[0].getHostName();
            // Tracker server http port
            SYS_FASTDFS_TRACKER_HTTP_PORT = ClientGlobal.getG_tracker_http_port() + "";
            //初始化FastDFS 结束
 
            String name = outfile.getName();
            // Format
            String extName = FileUtils.getFileExtension(name);
 
            //byte[] uploadFileBuffer = StreamUtils.InputStreamTOByte(item.getInputStream());
            //System.out.println("network_timeout=" + ClientGlobal.g_network_timeout + "ms");
            //System.out.println("charset=" + ClientGlobal.g_charset);
            // Save it into FastDFS
            TrackerClient tracker = new TrackerClient();
            TrackerServer trackerServer = tracker.getConnection();
            StorageServer storageServer = null;
            StorageClient client = new StorageClient(trackerServer, storageServer);
            // Set Meta data
            NameValuePair[] metaList = new NameValuePair[4];
            metaList[0] = new NameValuePair("fileName", name);
            metaList[1] = new NameValuePair("fileExtName", extName);
            metaList[2] = new NameValuePair("fileLength", String.valueOf(outfile.length()));
            metaList[3] = new NameValuePair("fileUid", this.getUserId());
            // Upload FastDFS
            //String[] fileIds = client.upload_file(local_filename, null, metaList);
            //String[] fileIds = client.upload_file(uploadFileBuffer, extName, metaList);
            long fileSize = outfile.length();
            UploadNetStreamCallback callback= new  UploadNetStreamCallback(new FileInputStream(outfile));
            String[] fileIds = client.upload_file(null, fileSize, callback, extName, metaList);
            for (int i = 0; i < fileIds.length; i++) {
               fileUrl = fileUrl + "/" + fileIds[i];
            }
            fileId = fileUrl.substring(1);
         } catch (Exception ex) {
            logger.error("FastDFS上传失败:",ex);
         }
         //已经由fastdfs存储,删除合并的文件
         saveDir.delete();
      }
      // 删除临时文件夹
      tempDir.delete();
      // 合并成功后删除缓存中的进度信息
      CacheUtils.remove(FILE_UPLOAD, "jindutiao_" + fileMd5);
      UploadifyResult result = new UploadifyResult();
      result.setFileId(fileId);//文件上传路径
      result.setFileUrl(fileUrl);//文件访问路径
      result.setPreShowPath(SHOW_PATH);
      return JSONObject.toJSONString(result);
   }
   @RequestMapping(value = "selectProgressByFileName")
   @ResponseBody
   // 当有文件添加进队列时 通过文件名查看该文件是否上传过 上传进度是多少
   public String selectProgressByFileName(String fileMD5) {
      String jindutiao = "";
      if (StringUtils.isNotBlank(fileMD5)) {
         jindutiao = ObjectUtils.toString(CacheUtils.get(FILE_UPLOAD, "jindutiao_" + fileMD5));
      }
//    System.out.println("显示进度条="+jindutiao);
      return "{jindutiao :'" + jindutiao + "'}";
   }
   /**
    * 获得分片文件临时保存路径
    *
    * @Title: getTempDir
    * @param tempPath 临时文件根目录
    * @param fileMd5 文件MD5
    * @return String 分片临时保存路径
    */
   private String getTempDir(String tempPath, String fileMd5) {
      StringBuilder dir = new StringBuilder(tempPath);
      dir.append(this.getUserId());
      dir.append(fileMd5);
      return dir.toString();
   }
   /**
    * 返回json格式
    * @param response
    * @param object
    */
   public void writeJSON(HttpServletResponse response, Object object){
      try {
         //设定编码
         response.setCharacterEncoding("UTF-8");
         //表示是json类型的数据
         response.setContentType("application/json");
         //获取PrintWriter 往浏览器端写数据
         PrintWriter writer = response.getWriter();
         ObjectMapper mapper = new ObjectMapper(); //转换器
         //获取到转化后的JSON 数据
         String json = mapper.writeValueAsString(object);
         //写数据到浏览器
         writer.write(json);
         //刷新,表示全部写完,把缓存数据都刷出去
         writer.flush();
         //关闭writer
         writer.close();
      } catch (IOException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
         logger.error("response中存json失败",e);
      }
   }
   protected String getUserId() {
      String sUid = "--";
      try {
         String sUidTmp = UserUtils.getUser().getId();
         if (null != sUidTmp && sUidTmp.trim().length() > 0) {
            sUid = sUidTmp.trim();
         }
      } catch (Exception ex) {
      }
      return sUid;
   }
}
UploadNetStreamCallback类:

org.csource.fastdfs包为FastDFS包,需要自行下载引用。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
 
import org.csource.fastdfs.UploadCallback;
 
public class UploadNetStreamCallback implements UploadCallback{
 
   private  InputStream is= null;
   
   public InputStream getIs() {
      return is;
   }
 
   public void setIs(InputStream is) {
      this.is = is;
   }
 
   public UploadNetStreamCallback(InputStream is) {
      super();
      this.is = is;
   }
   /**
   * send file content callback function, be called only once when the file uploaded
   * @param out output stream for writing file content
   * @return 0 success, return none zero(errno) if fail
   */
   
   public int send(OutputStream out) throws IOException {
 
      int readBytes;
      byte[] buff = new byte[8192];
      try {
         while ((readBytes = this.is.read(buff)) >= 0) {
            if (readBytes == 0) {
               continue;
            }
            out.write(buff, 0, readBytes);
         }
      } catch (IOException ex) {
         ex.printStackTrace();
         return -1;
      }
 
      return 0;
   }
 
}
UploadifyResult类:

public class UploadifyResult implements java.io.Serializable{
   private static final long serialVersionUID = 1L;
   
   private String fileId;
   private String fileName;
   private String fileUrl;
   private String preShowPath;//访问文件前缀
 
   public String getFileId() {
      return fileId;
   }
 
   public void setFileId(String fileId) {
      this.fileId = fileId;
   }
 
   public String getFileName() {
      return fileName;
   }
 
   public void setFileName(String fileName) {
      this.fileName = fileName;
   }
 
   public String getFileUrl() {
      return fileUrl;
   }
 
   public void setFileUrl(String fileUrl) {
      this.fileUrl = fileUrl;
   }
 
   public String getPreShowPath() {
      return preShowPath;
   }
 
   public void setPreShowPath(String preShowPath) {
      this.preShowPath = preShowPath;
   }
}

FileUtils类:

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Enumeration;
import java.util.List;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import com.google.common.collect.Lists;
 
/**
 * 文件操作工具类
 * 实现文件的创建、删除、复制、压缩、解压以及目录的创建、删除、复制、压缩解压等功能
 * @author 
 * @version 2015-3-16
 */
public class FileUtils extends org.apache.commons.io.FileUtils {
   
   private static Logger logger = LoggerFactory.getLogger(FileUtils.class);
 
   /**
    * 复制单个文件,如果目标文件存在,则不覆盖
    * @param srcFileName 待复制的文件名
    * @param descFileName 目标文件名
    * @return 如果复制成功,则返回true,否则返回false
    */
   public static boolean copyFile(String srcFileName, String descFileName) {
      return FileUtils.copyFileCover(srcFileName, descFileName, false);
   }
 
 
   /**
    * 复制整个目录的内容,如果目标目录存在,则不覆盖
    * @param srcDirName 源目录名
    * @param descDirName 目标目录名
    * @return 如果复制成功返回true,否则返回false
    */
   public static boolean copyDirectory(String srcDirName, String descDirName) {
      return FileUtils.copyDirectoryCover(srcDirName, descDirName,
            false);
   }
 
 
   /**
    * 
    * 删除文件,可以删除单个文件或文件夹
    * 
    * @param fileName 被删除的文件名
    * @return 如果删除成功,则返回true,否是返回false
    */
   public static boolean delFile(String fileName) {
      File file = new File(fileName);
      if (!file.exists()) {
         logger.debug(fileName + " 文件不存在!");
         return true;
      } else {
         if (file.isFile()) {
            return FileUtils.deleteFile(fileName);
         } else {
            return FileUtils.deleteDirectory(fileName);
         }
      }
   }
 
   /**
    * 
    * 删除单个文件
    * 
    * @param fileName 被删除的文件名
    * @return 如果删除成功,则返回true,否则返回false
    */
   public static boolean deleteFile(String fileName) {
      File file = new File(fileName);
      if (file.exists() && file.isFile()) {
         if (file.delete()) {
            logger.debug("删除文件 " + fileName + " 成功!");
            return true;
         } else {
            logger.debug("删除文件 " + fileName + " 失败!");
            return false;
         }
      } else {
         logger.debug(fileName + " 文件不存在!");
         return true;
      }
   }
 
   /**
    * 
    * 删除目录及目录下的文件
    * 
    * @param dirName 被删除的目录所在的文件路径
    * @return 如果目录删除成功,则返回true,否则返回false
    */
   public static boolean deleteDirectory(String dirName) {
      String dirNames = dirName;
      if (!dirNames.endsWith(File.separator)) {
         dirNames = dirNames + File.separator;
      }
      File dirFile = new File(dirNames);
      if (!dirFile.exists() || !dirFile.isDirectory()) {
         logger.debug(dirNames + " 目录不存在!");
         return true;
      }
      boolean flag = true;
      // 列出全部文件及子目录
      File[] files = dirFile.listFiles();
      for (int i = 0; i < files.length; i++) {
         // 删除子文件
         if (files[i].isFile()) {
            flag = FileUtils.deleteFile(files[i].getAbsolutePath());
            // 如果删除文件失败,则退出循环
            if (!flag) {
               break;
            }
         }
         // 删除子目录
         else if (files[i].isDirectory()) {
            flag = FileUtils.deleteDirectory(files[i]
                  .getAbsolutePath());
            // 如果删除子目录失败,则退出循环
            if (!flag) {
               break;
            }
         }
      }
      if (!flag) {
         logger.debug("删除目录失败!");
         return false;
      }
      // 删除当前目录
      if (dirFile.delete()) {
         logger.debug("删除目录 " + dirName + " 成功!");
         return true;
      } else {
         logger.debug("删除目录 " + dirName + " 失败!");
         return false;
      }
   }
   /**
    * 创建单个文件
    * @param descFileName 文件名,包含路径
    * @return 如果创建成功,则返回true,否则返回false
    */
   public static boolean createFile(String descFileName) {
      File file = new File(descFileName);
      if (file.exists()) {
         logger.debug("文件 " + descFileName + " 已存在!");
         return false;
      }
      if (descFileName.endsWith(File.separator)) {
         logger.debug(descFileName + " 为目录,不能创建目录!");
         return false;
      }
      if (!file.getParentFile().exists()) {
         // 如果文件所在的目录不存在,则创建目录
         if (!file.getParentFile().mkdirs()) {
            logger.debug("创建文件所在的目录失败!");
            return false;
         }
      }
      // 创建文件
      try {
         if (file.createNewFile()) {
            logger.debug(descFileName + " 文件创建成功!");
            return true;
         } else {
            logger.debug(descFileName + " 文件创建失败!");
            return false;
         }
      } catch (Exception e) {
         e.printStackTrace();
         logger.debug(descFileName + " 文件创建失败!");
         return false;
      }
   }
   /**
    * 创建目录
    * @param descDirName 目录名,包含路径
    * @return 如果创建成功,则返回true,否则返回false
    */
   public static boolean createDirectory(String descDirName) {
      String descDirNames = descDirName;
      if (!descDirNames.endsWith(File.separator)) {
         descDirNames = descDirNames + File.separator;
      }
      File descDir = new File(descDirNames);
      if (descDir.exists()) {
         logger.debug("目录 " + descDirNames + " 已存在!");
         return false;
      }
      // 创建目录
      if (descDir.mkdirs()) {
         logger.debug("目录 " + descDirNames + " 创建成功!");
         return true;
      } else {
         logger.debug("目录 " + descDirNames + " 创建失败!");
         return false;
      }
   }
   /**
    * 获取文件扩展名(返回小写)
    * @param fileName 文件名
    * @return 例如:test.jpg  返回:  jpg
    */
   public static String getFileExtension(String fileName) {
      if ((fileName == null) || (fileName.lastIndexOf(".") == -1) || (fileName.lastIndexOf(".") == fileName.length() - 1)) {
         return null;
      }
      return StringUtils.lowerCase(fileName.substring(fileName.lastIndexOf(".") + 1));
   }
   
}
以上为个人观点,与大家共同学习共同提高。

基于WebUploader、SpringMVC的断点续传相关推荐

  1. 基于Spring+SpringMVC+Mybatis分布式敏捷开发系统架构---权限管理系统

    原文链接: https://github.com/shuzheng/zheng 安利一个GitHub上的项目,见原文链接. 基于Spring+SpringMVC+Mybatis分布式敏捷开发系统架构, ...

  2. 基于Spring+SpringMvc实现的足球队管理系统

    项目编号:BS-XX-018 本项目基于Spring+Springmvc实现了一个足球队管理系统,系统功能完整,页面简洁大方,适合于毕业设计使用.下面展示一下系统的设计结构以及系统功能. 系统功能结构 ...

  3. 基于Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构(附源码)

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:推荐19个github超牛逼项目!个人原创100W +访问量博客:点击前往,查看更多 作者:zheng gitee ...

  4. 基于Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:又一程序员进了ICU:压垮一个家庭,一张结算单就够 个人原创100W+访问量博客:点击前往,查看更多 前言 本项 ...

  5. .NET或.NET Core Web APi基于tus协议实现断点续传

    [导读]前两天我采用技巧式方案基本实现大文件分片上传,这里只是重点在于个人思路和亲身实践,若在实际生产环境要求比较高的话肯定不行,仍存在一些问题需要深入处理,本文继续在之前基础上给出基于tus协议的轮 ...

  6. maven多模块项目部署到服务器,GitHub - baxias/foweb: 一个基于 Spring+SpringMVC+Mybatis 的Maven多模块项目。(实现前后端分离的服务器端)...

    Foweb Framework A multi-modules maven project base on Spring+SpringMVC+Mybatis. 一个基于 Spring+SpringMV ...

  7. 基于Spring+SpringMVC+Mybatis架构的开源博客

    介绍: Art_Blog   基于Spring+SpringMVC+Mybatis架构的开源博客:博客管理.图表数据.日志分析.访问记录.图库管理.资源管理.友链通知等.良好的页面预加载,无限滚动加载 ...

  8. 基于Spring+SpringMVC+Beetl的权限管理框架源码分享

    Java企业级开发平台源码 权限管理框架源码 源码分享! 基于Spring+SpringMVC+Beetl+Beetlsql+Shiro的权限管理框架.内置功能:用户管理.角色管理.菜单管理.字典管理 ...

  9. 基于Spring+SpringMVC+Mybatis分布式敏捷开发系统架构zhen项目

    原项目链接:https://github.com/shuzheng/zheng 目前看过做完整的项目,大家一起学习一下,详情请点击原项目链接的地址 前言 zheng项目创建于2016年10月4日,正在 ...

  10. 基于Spring+SpringMVC+Mybatis的Saas开源电商系统

    真正的大师,永远都怀着一颗学徒的心! 一.项目简介 基于Spring+SpringMVC+Mybatis的Saas开源电商系统 二.实现功能 支持店面管理 支持商品管理 支持用户管理 支持订单管理 支 ...

最新文章

  1. 每个程序员都应该收藏的算法复杂度速查表
  2. Redis-Cluster实战--4.官方安装(含视频)
  3. lk中内联调用的dsb()
  4. 教程 | 理解和实现自然语言处理终极指南(附Python代码)
  5. 你不知道LinkedList中的方法
  6. 备忘录怎么用红笔标注_错题本的正确打开方法,你用对了吗?
  7. nginx负载分发情况日志输出
  8. 怎么复活不了睡袋_测评 | 萌新的北京冬季户外睡袋初体验
  9. python字符数组输出_python字符串格式化输出
  10. linux下查看一个进程的启动时间和运行时间
  11. java字符替换函数示例_Java字符串替换函数replace()用法解析
  12. 在Word 2007中为公式编号
  13. RelativeLayout addRule踩坑之旅
  14. 计算机显示u盘隐藏分区,如何查看及删除u盘的隐藏分区
  15. 【3D动态脑图制作软件】万彩脑图大师教程 | 将思维导图输出到云服务
  16. Named Route ‘***‘ has a default child route. When navigating to this named route
  17. Hive基础知识及底层架构
  18. c++重写卷积网络的前向计算过程,完美复现theano的测试结果
  19. [OAuth2.0三方登录系列文章-1]OAuth2.0与三方登录的端到端方案
  20. 湍流模型(1)——湍流的统计描述

热门文章

  1. java uploadify 3.2_jquery文件批量上传控件Uploadify3.2(java springMVC)
  2. 机器学习实战(一)xgboost实战
  3. 焓湿图软件 android,焓湿图查询计算软件
  4. 韩顺平泰牛php2016,2016 泰牛程序员 韩顺平 PHP 大牛班 Div Css课程
  5. iOS开发之YYKit丰富的组件,如:YYText
  6. php框架 tp laravel,TP框架和Laravel框架的区别是什么
  7. keil5点击调试提示SarmCM3.dll文件缺少问题
  8. mysql 中的split函数_mysql中split函数
  9. macos 设置内外网同时访问
  10. dsp31段最佳调音图_均衡器如何使用_31段均衡器调整方法