木头人的gravatar头像
木头人 2018-01-10 23:18:14

webuploader实现文件断点分片并发上传实例

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

由于要做一个大文件上传,上传的文件支持几G以上,还要支持断点续传,就在网上找了一些资料。发现了几个功能还可以,这里先上传这个。由于网上好多代码不全,自己就结合网上的一些blog和官网的资料,代码终于跑起来了,还有一些功能需要扩展,比如使用数据库保存上传的进度,前台展示,下次上传不需要重新上传等等。下面直接上代码:

1.前端代码

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>webuploader上传</title>
<link rel="stylesheet" type="text/css" href="webuploader/webuploader.css">
<link rel="stylesheet" type="text/css" href="webuploader/bootstrap.css">
<script type="text/javascript" src="webuploader/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="webuploader/webuploader.js"></script>

</head>
<body>
	<div id="uploader" class="wu-example">
		<!--用来存放文件信息-->
		<div id="thelist" class="uploader-list"></div>
		<div class="btns">
			<div id="picker">选择文件</div>
			<button id="btn" class="btn btn-default">开始上传</button>
		</div>
	</div>
	<script type="text/javascript">
		var fileMd5;
		var $list=$("#thelist");
		var state = 'pending';//初始按钮状态
		var $btn=$("#btn");
		//监听分块上传过程中的三个时间点  
		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) {
							$('#' + file.id).find("p.state").text("正在读取文件信息...");
						}).then(function(val) {
							fileMd5 = val;
							$('#' + file.id).find("p.state").text("成功获取文件信息...");
							//获取文件信息后进入下一步  
							deferred.resolve();
						});
				return deferred.promise();
			},
			//时间点2:如果有分块上传,则每个分块上传之前调用此函数  
			beforeSend : function(block) {
				var deferred = WebUploader.Deferred();

				$.ajax({
					type : "POST",
					url : "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 : "video?action=mergeChunks",
					data : {
						fileMd5 : fileMd5,
					},
					success : function(response) {
						alert("上传成功");
					}
				});
			}
		});

		var uploader = WebUploader
				.create({
					// swf文件路径  
					swf : 'webuploader/Uploader.swf',
					// 文件接收服务端。  
					server : 'upload',
					// 选择文件的按钮。可选。  
					// 内部根据当前运行是创建,可能是input元素,也可能是flash.  
					pick : {
						id : '#picker',//这个id是你要点击上传文件的id
						multiple : false
					},
					// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!  
					resize : true,
					auto : true,
					//开启分片上传  
					chunked : true,
					chunkSize : 10 * 1024 * 1024,

					accept : {
						extensions : "txt,jpg,jpeg,bmp,png,zip,rar,war,pdf,cebx,doc,docx,ppt,pptx,xls,xlsx",
						mimeTypes : '.txt,.jpg,.jpeg,.bmp,.png,.zip,.rar,.war,.pdf,.cebx,.doc,.docx,.ppt,.pptx,.xls,.xlsx'
					}
				});

		// 当有文件被添加进队列的时候  
		uploader.on('fileQueued', function(file) {
			$list.append(
					'<div id="' + file.id + '" class="item">'
							+ '<p class="info">' + file.name + '</p>'
							+ '<p class="state">等待上传...</p></div>');
		});

		// 文件上传过程中创建进度条实时显示。  
		uploader.on('uploadProgress', function(file, percentage) {
			$('#' + file.id).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();
		});
		
		
		uploader.on('all', function(type) {
			if (type === 'startUpload') {
				state = 'uploading';
			} else if (type === 'stopUpload') {
				state = 'paused';
			} else if (type === 'uploadFinished') {
				state = 'done';
			}

			if (state === 'uploading') {
				$btn.text('暂停上传');
			} else {
				$btn.text('开始上传');
			}
		});
		
		$btn.on('click', function(){  
	        if (state === 'uploading'){  
	            uploader.stop(true);  
	        } else {  
	        	uploader.upload();
	        }
		});
	</script>
</body>
</html>

webuploader实现文件断点分片并发上传实例

2.实现上传的功能

package com.cn.upload;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;

/**
 * 文件上传
 * @author Administrator
 *
 */
@SuppressWarnings("serial")
public class UploadVideo extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		super.doGet(req, resp);
		doPost(req, resp);
	}

	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) {
			e.printStackTrace();
		}

	}
}

3.实现分片合并

package com.cn.upload;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
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.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 合并分块
 * @author Administrator
 *
 */
@SuppressWarnings("serial")
public class Video extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		super.doGet(request, response);
		doPost(request, response);

	}

	@SuppressWarnings("resource")
	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() {
				// 排除目录只要文件
				public boolean accept(File pathname) {
					if (pathname.isDirectory()) {
						return false;
					}
					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;
					}
					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}");
			}
		}

	}

}

打赏

已有1人打赏

最代码官方的gravatar头像

文件名:webuploader.zip,文件大小:597.136K 下载
  • /
      • /webuploader
        • /webuploader/.classpath
        • /webuploader/.project
          • /webuploader/.settings
            • /webuploader/.settings/.jsdtscope
            • /webuploader/.settings/org.eclipse.jdt.core.prefs
            • /webuploader/.settings/org.eclipse.wst.common.component
            • /webuploader/.settings/org.eclipse.wst.common.project.facet.core.xml
            • /webuploader/.settings/org.eclipse.wst.jsdt.ui.superType.container
            • /webuploader/.settings/org.eclipse.wst.jsdt.ui.superType.name
最代码最近下载分享源代码列表最近下载
sl0018  LV13 2023年2月7日
su12345su  LV8 2022年5月8日
李海洋  LV12 2021年11月29日
bnna8356586  LV1 2021年8月22日
tomhat  LV3 2021年4月20日
sdfssf  LV1 2021年3月23日
massacre  LV1 2020年9月9日
bhf1030  LV2 2020年6月9日
wxxazm  LV3 2020年6月8日
momomo2  LV9 2020年4月13日
最代码最近浏览分享源代码列表最近浏览
sl0018  LV13 2023年2月7日
su12345su  LV8 2022年5月8日
taoshen95  LV14 2022年4月13日
李海洋  LV12 2021年11月29日
azhan2021 2021年8月23日
暂无贡献等级
bnna8356586  LV1 2021年8月22日
里更debug  LV10 2021年8月9日
newhaijun  LV15 2021年7月2日
最大码LV1789554  LV1 2021年6月24日
InventorLee 2021年5月20日
暂无贡献等级
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友