java使用WebUploader做大文件的分块和断点续传

前言:

WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。

 

上面的一段话是来自 http://fex.baidu.com/webuploader/  的介绍,现在做的项目需要用到大文件的上传,之前没有做过,现有的jquery的uploadify只用于上传图片什么的小文件,查了网上的资料,这个插件好像对于大文件不是很友好,为了安全起见,使用百度的成熟框架,不论是多文件还是单个的大文件都是很好用的,没有很多的问题,关于webuploader的详细介绍看官网就行:

 

我的项目是javaweb,开发环境是MyEclipse,页面使用jsp

 

1、先将 webuploader-0.1.5.zip 这个文件下载下来:https://github.com/fex-team/webuploader/releases  

根据个人的需求放置自己需要的东西就行,全部放到项目里也可以,下面是我自己需要的东西:

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


1、jsp部分代码:

 

<script type="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('<div id="' + file.id + '" class="item">'+
		    	'<a class="upbtn" id="btn" οnclick="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>

 

 

 

2、servlet部分代码:


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

1、接收分块servlet代码:

 

@SuppressWarnings("serial")
public class UploadVideo extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
		doPost(req, resp);
	}
    @SuppressWarnings("unchecked")
    public void 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")
public class Video extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		super.doGet(request, response);
		doPost(request, response);
		
	}

	public void 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
				public boolean accept(File pathname) {
					// TODO Auto-generated method stub
					if(pathname.isDirectory()){
						return false;
					}
					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) {
					// 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("合并成功");
		}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}");
			}
		}
		
	}

}

 

 

 

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

github地址:https://github.com/panzhoutao/BigFileWebUploader

 

评论 63
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值