百度Web Uploader组件实现文件上传之分片上传(一)

[color=red][size=large]
当网络问题导致传输错误时,只需要重传出错分片,而不是整个文件。另外分片传输能够更加实时的跟踪上传进度。多的不说了直接怼代码[/size][/color]

[size=large]前端是三个监听:一个是获取md5,一个是分片,最后一个是合并代码[/size]
<!DOCTYPE html>
<html ng-app="uploadApp" ng-controller="uploadCtl">
<head>
<title>fileupload.html</title>

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="../themes/reset.css" type="text/css"></link>
<link rel="stylesheet" href="../themes/webuploader.css" type="text/css"></link>
<script type="text/javascript" src="../plugins/jquery-3.2.0.min.js"></script>
<script type="text/javascript" src="../plugins/webuploader.min.js"></script>
<script type="text/javascript">
$(function(){
var fileName;
var fileMd5;
//监听分块上传过程中的三个时间点
WebUploader.Uploader.register({
"before-send-file":"beforeSendFile",
"before-send":"beforeSend",
"after-send-file":"afterSendFile",
},{
//时间点1:所有分块进行上传之前调用此函数
beforeSendFile:function(file){
fileName=file.name;
var deferred = WebUploader.Deferred();
//1、使用md5计算文件的唯一标记,用于断点续传
(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();
});
//调用deferred.resolve();无效
return deferred.promise();
},
//时间点2:如果有分块上传,则每个分块上传之前调用此函数
beforeSend:function(block,file){
console.log("分块"+fileName.replace(/.+\./, ""));
var deferred = WebUploader.Deferred();

$.ajax({
type:"POST",
url:"/servlet/mergeFile?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(){
console.log("合并") ;
//如果分块上传成功,则通知后台合并分块
$.ajax({
type:"POST",
url:"/servlet/mergeFile?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: '/servlet/FileUpLoad',
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: {id: '#picker',
//这个id是你要点击上传文件的id,自己设置就好</span>
multiple:true},
// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
resize: true,
auto:true,
//上传并发数
threads:5,
//开启分片上传
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="state">等待上传...</div>'
);
});

// 文件上传过程中创建进度条实时显示。
uploader.on( 'uploadProgress', function( file, percentage ) {
$('#item1').find('p.state').text(file.name+'上传中 '+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>
</head>
<body>
<div id="uploader" class="wu-example">
<!--用来存放文件信息-->
<div id="thelist" class="uploader-list"></div>
<div class="btns">
<div id="picker">选择文件</div>
<button id="ctlBtn" class="btn btn-default">开始上传</button>
</div>
</div>
<div id="item1"></div>
</body>
</html>


后端:一个保持文件servlet,一个判断分片和合并文件servelt
[size=x-large]
保持文件servlet[/size]

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Path;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
@Path("/fileupload")
public class ZFileCommand extends HttpServlet{
private static final long serialVersionUID = -2720014423604662780L;
// 1.文件上传路径
private static final String UPLOAD_DIRECTORY = "D:/文件上传";
// 2.设置临时存储文件大小,当超过大小时,将先存储超出大小文件在临时目录
private static final int MEMORY_THRESHOLD = 1024 * 1024 * 30;
// 3.设置最大文件上传值
private static final int MAX_FILE_SIZE = 1024 * 1024 * 2000;
// 4.最大请求值
private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 2048;

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取文件名
String filename=request.getParameter("name");
//防止读取name名乱码
filename=new String(filename.getBytes("iso-8859-1"),"utf-8");
//在控制台打印文件名
System.out.println("文件名:"+filename);
//设置文件MIME类型
response.setContentType(getServletContext().getMimeType(filename));
//设置Content-Disposition
String realName = filename.substring(filename.indexOf("_")+1);
response.setHeader("Content-Disposition", "attachment;filename="+realName);

//输入流为项目文件,输出流指向浏览器
InputStream is=new FileInputStream(UPLOAD_DIRECTORY+filename);
ServletOutputStream os =response.getOutputStream();
/*
* 设置缓冲区
* is.read(b)当文件读完时返回-1
*/
int len=-1;
byte[] b=new byte[1024];
while((len=is.read(b))!=-1){
os.write(b,0,len);
}
//关闭流
is.close();
os.close();

}

/**
* @摘要 提供文件上传的方法
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
//1.设置字符编码为utf-8
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
// 2.检测是否为多媒体上传
if (!ServletFileUpload.isMultipartContent(request)) {
// 2.1如果不是则停止
PrintWriter writer = response.getWriter();
writer.println("Error: 表单必须包含 enctype=multipart/form-data");
writer.flush();
return ;
}
// 3.配置上传参数
DiskFileItemFactory factory = new DiskFileItemFactory();
//4. 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
factory.setSizeThreshold(MEMORY_THRESHOLD);
// 5.设置临时存储目录 java.io.tmpdir默认的临时文件路径为服务器的temp目录
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

ServletFileUpload upload = new ServletFileUpload(factory);

// 6.设置最大文件上传值
upload.setFileSizeMax(MAX_FILE_SIZE);

// 7.设置最大请求值 (包含文件和表单数据)
upload.setSizeMax(MAX_REQUEST_SIZE);

//8. 如果目录不存在则创建
File uploadDir = new File(UPLOAD_DIRECTORY);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
String fileMd5 = null;
String chunk = null;
try {
// 10.解析请求的内容提取文件数据
List<FileItem> formItems = upload.parseRequest(request);
// 10.1迭代表单数据
if (formItems != null && formItems.size() > 0) {
for (FileItem item : formItems) {
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{
String nFileName = new File(item.getName()).getName();

File file = new File(UPLOAD_DIRECTORY+"/"+fileMd5);

if(!file.exists()){
file.mkdir();
}
nFileName=nFileName.substring(0,nFileName.lastIndexOf(".")) ;

item.write(new File(UPLOAD_DIRECTORY+"/"+fileMd5+"/"+chunk));

item.delete();
}

}
}
} catch (Exception ex) {
PrintWriter writer=response.getWriter();
writer.print("error");
}
}
}


[size=large]一个判断分片和合并文件servelt[/size]
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;

public class mergeFile extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String UPLOAD_DIRECTORY = "D:/文件上传";

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 = "D:/文件上传";

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) {
// 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>() {
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}");
}
}

}

[size=x-large]附件:源码[/size]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值