web前端分片上传demo(基于webuploader)

首先简短解释下断点续传的原理:

     客户端选取文件,通过webuploader进行MD5然后进行分片,每一个分片均需要进行上传前检查(检查当前分片时候已经上传),如果没有上传就开始进行上传,最后在上传完成之后会调用一个合并的操作,在后台将所有分片依次合并,按照指定文件名生成文件。

1.准备工作:

     1)前端使用jquery(jquery-1.10.2.js),webuploader插件(webuploader-0.1.5)

     2)后台准备 springMvc(使用注解,配置requestMapping)

    3)开发环境 jsp,MyEclipse 1.0

2.开发步骤:

    1)集成springMvc,此处步骤相对简单(可按照: http://www.jianshu.com/p/0ccaa4af05fc)。

    2)导入jquery , webuploader,编写前台页面,代码如下:

  

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ include file="/root/comm/jsp/tags.jsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GBK">
<title>断点续传</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script type="text/javascript"	src="${ctx}/root/comm/js/jquery-1.10.2.js"></script>
<script type="text/javascript"	src="${ctx}/root/comm/webuploader-0.1.5/webuploader.js"></script>
<link rel="stylesheet" type="text/css"	href="${ctx}/root/comm/webuploader-0.1.5/webuploader.css">

<script type="text/javascript">
	// 监听分块上传的时间点,断点续传
	var fileMd5;    //保存文件MD5名称
	var uploader;   //全局对象uploader
	WebUploader.Uploader.register(
					{"before-send-file" : "beforeSendFile", //文件上传之前执行
					 "before-send" : "beforeSend",		//文件块上传之前执行
					 "after-send-file" : "afterSendFile",   //上传完成之后执行
					},
					{
						//时间点1:所有进行上传之前调用此函数
						beforeSendFile : function(file) {
							var deferred = WebUploader.Deferred();
							// 计算文件的唯一标识,用于断点续传和妙传
							(new WebUploader.Uploader()).md5File(file, 0,
									10 * 1024 * 1024).progress(
									function(percentage) {
										$("#" + file.id).find("span.state").text("正在获取文件信息...");
									}).then(
									function(val) {
										fileMd5 = val;
										$("#" + file.id).find("span.state").text("成功获取文件信息");
										// 放行
										deferred.resolve();
									});
							return deferred.promise();
						},
						//每一个分块发送之前执行该操作,检查当前块是否已经上传
						beforeSend : function(block) {
							var deferred = WebUploader.Deferred();
							$.ajax({
										type : "POST",
										url : "${ctx}/system/upload/testUploadMergeUploadFile.htm?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;
							return deferred.promise();
						},
						afterSendFile : function(file) {
							// 通知合并分块
							 $.ajax({
									type : "POST",
									url : "/RenWei/system/upload/testUploadMergeUploadFile.htm?action=mergeChunks",
									data : {
										'fileMd5' : fileMd5
									},
									success : function(response) {
										var $li = $('#' + file.id);
										$li.find('p.state').text('上传完成');
										$("#ctlBtn").addClass('disabled');
									}
									}); 
						}
					});

	$(function() {
		// 上传基本配置
		uploader = WebUploader.create({
			swf : "${ctx}/root/comm/webuploader-0.1.5/Uploader.swf",
			server : '${ctx}/system/upload/testUploadSaveFile.htm',
			pick : "#filePicker",
			auto : false,
			// 分块上传设置
			// 是否分块
			chunked : true,
			// 每块文件大小(默认5M)
			chunkSize : 10 * 1024 * 1024,
			// 开启几个并非线程(默认3个)
			threads : 1,
			// 在上传当前文件时,准备好下一个文件
			formData : { //这里可以自定义属性,在后台全部都可以接收
				fileMd5 : fileMd5
			},
			prepareNextFile : true
		});

		// 选中文件之后触发
		uploader.on("fileQueued", function(file) {
			// 把文件信息追加到fileList的div中
			$("#fileList").append(
					'<div id="' + file.id + '" class="item">'
							+ '<h4 class="info">' + file.name + '</h4>'
							+ '<p class="state">等待上传...</p>' + '</div>');
			//md5计算
			uploader.md5File(file).progress(function(percentage) {
				console.log('Percentage:', percentage);
			})
			// 完成
			.then(function(md5File) { // 完成
				fileMd5 = md5File;
				$('#' + file.id).find('p.state').text('MD5计算完毕,可以点击上传了');
				$("#ctlBtn").removeClass('disabled');
				
			});
		});

		// 监控上传进度
		// percentage:代表上传文件的百分比
		// 文件上传过程中创建进度条实时显示。
		uploader.on('uploadProgress', function(file, percentage) {
			var $li = $('#' + file.id);
			$li.find('p.state').text('上传中 '+Math.round(percentage * 100) + '%'); 
		});

		$("#ctlBtn").on('click', function() {
			if ($(this).hasClass('disabled')) {
				alert("请选择文件或等待文件生成MD5完成。");
				return false;
			}
			uploader.options.formData['fileMd5'] = fileMd5;
			uploader.upload();
		});
	});
</script>
</head>
<body>
	<!-- 2.创建页面元素 -->
	<div id="upload">
		<div id="fileList"></div>
		<div id="filePicker" style="float: left;">选择文件</div>
		<button id="ctlBtn" class="btn btn-default disabled" style="padding: 8px 15px;">开始上传</button>
		<div class="statusBar" style="display: none;">
			<div class="progress">
				<span class="text">0%</span> <span class="percentage"></span>
			</div>
			<div class="info"></div>
		</div>
	</div>
</body>
</html>

 -----------------------------------------------------------------华丽的分割线--------------------------------------------------

至此前台代码已经完成:

  3)开始编写后台代码:

@Controller
@RequestMapping("/system/upload")
public class TestUpload {
	
	String serverPath = "d:\\";  //临时目录
	/*
	 * 保存文件,数据块
	 */
	@SuppressWarnings("unchecked")
	@RequestMapping("testUploadSaveFile.htm")
	public void saveFile(HttpServletRequest request,
			HttpServletResponse response) {
		
		DiskFileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		
		try {
			List<FileItem> items = upload.parseRequest(request);
			
			UploadUtil.saveChunkData(items);
			
		} catch (FileUploadException e) {
			
			e.printStackTrace();
		}
	}
	
	//合并上传文件
	@RequestMapping("testUploadMergeUploadFile.htm")
	public void mergeUploadFile(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("进入合并后台...");
        
        String action = request.getParameter("action");
        String saveFilePath = ConfigUtil.getInstance().getProValue("saveChunkFilePath");
    	String fileMd5 = request.getParameter("fileMd5");
    	
        if("mergeChunks".equals(action)){
        	
        	UploadUtil.mergeUploadFile(saveFilePath, fileMd5);
        }
        else if("checkChunk".equals(action)){
            // 当前分块下标
            String chunk = request.getParameter("chunk");
            // 当前分块大小
            String chunkSize = request.getParameter("chunkSize");
           String yesOrNoFlag =  UploadUtil.checkChunk(saveFilePath+"/"+fileMd5, chunk, chunkSize);
           
           if(yesOrNoFlag.equals("1")){
        	   response.getWriter().write("{\"ifExist\":"+yesOrNoFlag+"}"); 
        	   }
           else{
        	   response.getWriter().write("{\"ifExist\":"+yesOrNoFlag+"}"); 
           }
           }
        }
}

    具体的上传方法我已经抽象出来了(上述代码中的UploadUtil.mergeUploadFile(saveFilePath, fileMd5),UploadUtil.saveChunkData(items))两个方法。

),

    代码如下: 

       哟,抽象出来的还是放在附件中了(UploadUtil.java),这样大家想用的话直接复制就好。^_^^_^^_^^_^^_^

好了,到目前未知我们所有的工作都已经完成了,就可以直接将代码发布在中间件上,直接查看方才编写的

jsp了。

正常情况下都是可以上传成功的,并且有进度条显示上传的进度。

如果不成功的话,可参照一下步骤检查:

  1.WebUploader.Uploader.register (1.2.3) 三个方法必须放置在最前边,且不能放置在$(function(){...})

中,都则1.2.3三个方法不生效,原因是作用域链问题。

  2.同一个分片多次上传仍然覆盖问题,每一个分片检查完成之后的放行,阻断方法问题,deferred.reject();

deferred.resolve();  主要是deferred 延迟对象在检查完成之后不管结果全部放行。解决方案: 严格按照我代码中的写法,就没有问题。

最后如果还有什么问题,比如发现代码中逻辑错误,不够严谨,都可以直接联系我,

微信: ma0603kang

相关推荐