webuploader大文件 分片 断点续传

时间:2023-01-01 13:19:05
原文参考于http://blog.csdn.net/chenxiaoyu_csdn/article/details/70847203
其中修复了原文章中由于网络中断等问题,断点续传时会再次上传已经上传文件的问题.修复了不需要分片的文件,上传过程中会出现异常的问题.

  1. webupload官网下载需要的Uploader.swf、webuploader.css、webuploader.js  文件
  2. jsp 页面
                         <div id="uploadfile">
			                <!--用来存放文件信息-->
			                <div id="thelist" class="uploader-list"></div>
			                <div class="form-group form-inline">
			                    <div id="picker">选择文件</div>
			                    <button id="ctlBtn" class="btn btn-default">开始上传</button>
			                </div>
			                <p style="color: red;margin-top: 80px;" id="fileError"></p>
			            </div>
3.js 代码
<script type="text/javascript">
   //var projcetName =$("#project").val();
  $(function(){
	var $list = $('#thelist'),
        $btn = $('#ctlBtn');
 	
	var fileMd5;
	//自定义参数 文件名
	var fileName;
    //监听分块上传过程中的三个时间点  
    WebUploader.Uploader.register({  
        "before-send-file":"beforeSendFile",  //整个文件上传前调用
        "before-send":"beforeSend",  //每个分片上传前
        "after-send-file":"afterSendFile",  //分片上传完毕
    },{  
        //时间点1:所有分块进行上传之前调用此函数  
        beforeSendFile:function(file){  
        	console.log("beforeSendFile");
            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){
        	console.log("beforeSend");
            var deferred = WebUploader.Deferred();  
              
            $.ajax({  
                type:"POST",  
                url:"checkOrMerge?action=checkChunk",  
                data:{  
                    //文件唯一标记  
                    fileMd5:fileMd5,  
                    //当前分块下标  
                    chunk:block.chunk,  
                    //当前分块大小  
                    chunkSize:block.end-block.start  
                },  
                dataType:"json",  
                success:function(response){  
                    if(response.ifExist){ 
                    	console.log("分片存在:"+block.chunk)
                        //分块存在,跳过  
                        deferred.reject();  
                    }else{
                    	console.log("分片不存在:"+block.chunk)
                        //分块不存在或不完整,重新发送该分块内容  
                        deferred.resolve();  
                    }  
                }  
            });  
              
            this.owner.options.formData.fileMd5 = fileMd5;
            console.log("继续执行")
            //deferred.resolve();  
            return deferred.promise();  
        },  
        //时间点3:所有分块上传成功后调用此函数  
        afterSendFile:function(file){
        	console.log("afterSendFile");
        	fileName=file.name; //为自定义参数文件名赋值
            //如果分块上传成功,则通知后台合并分块  
            $.ajax({  
                type:"POST",  
                url:"checkOrMerge?action=mergeChunks",  
                data:{  
                    fileMd5:fileMd5,
                    fileName:fileName,
                    ext:file.ext, //文件扩展名
                    projectName: $("#project option:selected").text() //项目名称
                },  
                success:function(response){
                	var data= eval('(' + response + ')');
                	var filePath = data.path;
                	console.log(filePath);
                	//存储文件路径
                	var path=$("#filePath").val();
            		if(path!=""){
            			path=path+"|"+filePath;
            		}else{
            			path=filePath;
            		}
            		$("#filePath").val(path);
                    //alert("上传成功");  
                    //var path = "uploads/"+fileMd5+".mp4";  
                    //$("#item1").attr("src",path);  
                }  
            });  
        }  
    });  
	
	
    var uploader = WebUploader.create({
      resize: false, // 不压缩image     
      swf: '/res/webuploader-0.1.5/uploader.swf', // swf文件路径
      formData: { projectName: ""},
      server: 'upload', // 文件接收服务端。
      pick: '#picker', // 选择文件的按钮。可选
      chunked: true, //是否要分片处理大文件上传
      chunkSize:10*1024*1024, //分片上传,每片2M,默认是5M
      
      //auto: false //选择文件后是否自动上传
     chunkRetry : 2, //如果某个分片由于网络问题出错,允许自动重传次数
      //runtimeOrder: 'html5,flash',
      // 在上传当前文件时,准备好下一个文件
 	  prepareNextFile:true,
 	  duplicate : false,//是否重复上传(同时选择多个一样的文件),true可以重复上传  
       accept: {
         title: '语音上传',
         extensions: 'wav,zip,rar',
         mimeTypes: 'audio/x-wav,.zip,.rar'
       }
    });
  //当文件被加入队列之前触发
    uploader.on('beforeFileQueued', function (file,data) {
    	//项目名称,在后台作为文件夹路径
    	var projcetName =$("#project").val();
    	if(projcetName==""){
    		$("#projectError").text("请先选择项目名称再上传文件!");
    		return false;
    	}else{
    		$("#projectError").text("");
    	}
    	//选定项目名称后 ,不可更改
    	$("#project").attr("disabled","disabled");
    });
    // 当有文件被添加进队列的时候
    uploader.on( 'fileQueued', function( file ) {
        $list.append( '<div id="' + file.id + '" class="item">' +
            '<span class="info">' + file.name + '</span>' +
            '<b class="state" style="width:90px;">等待上传...</b>' +
        '</div>' );
    });
    //绑定uploadBeforeSend事件来给每个独立的文件添加参数
    uploader.on( 'uploadBeforeSend', function( block, data ) {
    	//设置data参数
    	data.projectName= $("#project").find("option:selected").text();   // 将存在file对象中的md5数据携带发送过去。
	},2);
    // 文件上传过程中创建进度条实时显示。
    uploader.on( 'uploadProgress', function( file, percentage ) {
        var $li = $( '#'+file.id ),
            $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%">' +
              '</div>' +
            '</div>').appendTo( $li ).find('.progress-bar');
        }
		//进度条以百分比的形式显示
        $li.find('b.state').text('上传中'+Math.floor(percentage * 100) + '%' );

        $percent.css( 'width', percentage * 100 + '%' );
    });
    // 文件上传成功
    uploader.on( 'uploadSuccess', function( file,ret) {
    	//返回文件的保存路径
    	if(ret.flag==true){
    		console.log(ret.path);
    		var path=$("#filePath").val();
    		if(path!=""){
    			path=path+"|"+ret.path;
    		}else{
    			path=ret.path;
    		}
    		$("#filePath").val(path);
    	}
        $( '#'+file.id ).find('b.state').text('上传成功');
        $( '#'+file.id ).find('b.state').css("color","green");
    });

    // 文件上传失败,显示上传出错
    uploader.on( 'uploadError', function( file,ret ) {
    		status=false;
        $( '#'+file.id ).find('b.state').text('上传失败');
    });
    // 完成上传完
    uploader.on( 'uploadComplete', function( file ) {
        $( '#'+file.id ).find('.progress').fadeOut();
    });

    $btn.on('click', function () {
            if ($(this).hasClass('disabled')) {
                return false;
            }
            uploader.upload();
            // if (state === 'ready') {
            //     uploader.upload();
            // } else if (state === 'paused') {
            //     uploader.upload();
            // } else if (state === 'uploading') {
            //     uploader.stop();
            // }
        });

});
  </script>  

4. 后台文件上传及合并代码

	// 上传文件
	@RequestMapping(value = "upload")
	public void uploadFile(HttpServletRequest request,
			HttpServletResponse response, String projectName)
			throws IOException {
		response.setCharacterEncoding("UTF-8");
		Map map = new HashMap<>();
		MultipartFile uploadFile = ((MultipartHttpServletRequest) request)
				.getFile("file");
		String fileMd5 = request.getParameter("fileMd5");
		String chunk = request.getParameter("chunk");
		String path = audioPath;
		File file = new File(path + fileMd5);
		if (!file.exists()) {
			file.mkdirs();// 创建文件夹
		}
		// 保存文件
		File chunkFile = new File(path + fileMd5 + "/" + chunk);
		if (!chunkFile.exists()) {
			chunkFile.createNewFile();
		}
		uploadFile.transferTo(chunkFile);
	}

	// 合并或验证分片文件是否需要上传
	@RequestMapping(value = "checkOrMerge")
	public void checkOrMerge(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		response.setCharacterEncoding("UTF-8");
		String savePath = audioPath;

		String action = request.getParameter("action");

		if (action.equals("mergeChunks")) {
			// 合并文件
			// 需要合并的文件的目录标记
			// 文件MD5
			String fileMd5 = request.getParameter("fileMd5");
			// 文件名称
			String fileName = request.getParameter("fileName");
			// 文件扩展名
			String suffixName = request.getParameter("ext");
			// 项目名称
			String projectName = request.getParameter("projectName");
			System.out.println("fileMd5  :" + fileMd5);
			System.out.println("fileName  :" + fileName);
			System.out.println("projectName  :" + projectName);
			// 读取目录里的所有文件
			File f = new File(savePath + fileMd5);
			File[] fileArray = f.listFiles(new FileFilter() {
				// 排除目录只要文件
				@Override
				public boolean accept(File pathname) {
					if (pathname.isDirectory()) {
						return false;
					}
					return true;
				}
			});
			System.out.println(" fileArray " + fileArray);
			// 转成集合,便于排序
			List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));
			// 需要合并的文件才进行排序,即分片的大小大于1
			if (fileList != null && fileList.size() > 1) {
				Collections.sort(fileList, new Comparator<File>() {
					@Override
					public int compare(File o1, File o2) {
						// TODO Auto-generated method stub
						if (Integer.parseInt(o1.getName()) < Integer
								.parseInt(o2.getName())) {
							return -1;
						}
						return 1;
					}
				});
			}
			// 合并的文件夹
			File mergeFile = new File(savePath + projectName);
			if (!mergeFile.exists()) {
				mergeFile.mkdirs();
			}
			// UUID.randomUUID().toString()-->随机名
			File outputFile = new File(savePath + projectName + "/" + fileName);
			// 创建文件
			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("合并成功");
			Map<String, String> map = new HashMap<>();
			// 文件路径
			map.put("path", projectName + "/" + fileName);
			response.getWriter().print(JSON.toJSON(map));
		} else if (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}");
			}
		}

	}