一.使用webuploader插件的原因说明被现在做的项目坑了。 先说一下我的项目架构Spring+struts2+mybatis+mysql 然后呢。之前说好的按照2G上传就可以了,于是乎,用了ajaxFileUpload插件,因为之前用图片上传也是用这个,所以上传附件的时候就直接拿来用了 各种码代码,测试也测过了,2G文件上传没问题,坑来了,项目上线后,客户又要求上传4G文件,甚至还有20G以上的。。纳尼,你不早说哦。。。 在IE11下用ajaxFileUpload.js插件上传超过4G的文件,IE直接抛出异常了。弹出 算术结果超过32位 的消息. 如下图: 附加说明一下,我的系统是64位,8G内存,google浏览器和IE11浏览器都是32位的。google下用AjaxFileUpload上传8G都问题。都不会报错。 IE11下超过4G直接报上图这个错了。没办法。换插件。 二.插件选择1.stream上传插件。stream是解决不同浏览器上传文件插件,是Uploadify的flash版和html5的结合。插件地址http://www.twinkling.cn/ 功能确实很强大,不过CSS样式固定死了,和我现在项目的进度条样式很不一样。还是放弃了这个插件 2.Webuploader 插件。WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。 采用大文件分片并发上传,极大的提高了文件上传效率。插件地址 http://fex.baidu.com/webuploader/ 这个插件可以自定义CSS样式啊。功能也很强大,于是乎果断采用这个插件。安利一个webuplolder的作者写的使用webupload的上传优化总结。建议在了解了webuploader之后在看,会有意想不到的收获: http://www.sxrczx.com/pages/itindex.net/hmhk1423492392082.html 三.WebUploader 单文件上传我用的是Webuploader0.1.5版本的,Webuploader主要是把大文件在客户端进行分片,比如按照每5M进行分片发送请求,后台接收到文件进行合并文件。两种方式合并文件,第一种等所有分片都传到后台,然后在合并,这种要保障分片顺序正确,第二种是边分片边合并。项目里我使用的是第二种。使用Web Uploader文件上传需要引入三种资源:JS, CSS, SWF。 1.引入JS文件 <script type="text/javascript" src="../main/js/webuploader.js"></script>
<script type="text/javascript" src="../main/js/webuploader.min.js"></script>
2.引入CSS样式 <link href="../main/css/webuploader.css" rel="stylesheet" type="text/css" />
3.引入SWF,SWF不直接引用,在webUploader初始化的时候指定SWF的路径就可以了。参考后面的代码。 4.upload3.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta http-equiv="Content-Language" content="ja" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>DEMO</title>
<link href="../main/css/stream-v1.css" rel="stylesheet" type="text/css" />
<link href="../main/css/webuploader.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../main/js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="../main/js/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="../main/js/jquery-ui.min.js"></script>
<script type="text/javascript" src="../main/js/bootstrap-datepicker.min.js"></script>
<script type="text/javascript" src="../main/js/locales/bootstrap-datepicker.ja.min.js"></script>
<script type="text/javascript" src="../main/js/webuploader.js"></script>
<script type="text/javascript" src="../main/js/webuploader.min.js"></script>
<script type="text/javascript" src="../js/contents/upload3.js"></script>
</head>
<body>
<div id="uploader" class="wu-example">
<!--用来存放文件信息-->
<div id="thelist" class="uploader-list"></div>
<div class="btns">
<div id="attach"></div>
<input type="button" value="上传" id="upload"/>
</div>
</div>
<div id="uploader1" class="wu-example">
<!--用来存放文件信息-->
<div id="thelist1" class="uploader-list"></div>
<div class="btns">
<div id="multi"></div>
<input type="button" value="上传" id="multiUpload"/>
</div>
</div>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
画面比较简单,长这个样子
5.upload3.js 包含单文件上传,多文件上传,和webuploader多实例,webuploader多实例就是把webuploader初始化多遍,webuploader的官网上的范例就是多实例。 /*********************************WebUpload 单文件上传 begin*****************************************/
$(function(){
var $list = $("#thelist");
var uploader ;// 实例化
uploader = WebUploader.create({
auto:false, //是否自动上传
pick: {
id: '#attach',
name:"file", //这个地方 name 没什么用,虽然打开调试器,input的名字确实改过来了。但是提交到后台取不到文件。如果想自定义file的name属性,还是要和fileVal 配合使用。
label: '点击选择图片',
multiple:false //默认为true,true表示可以多选文件,HTML5的属性
},
swf: '../../main/js/Uploader.swf', //在这里必需要引入swf文件,webuploader初始化要用
//fileVal:'multiFile', //自定义file的name属性,我用的版本是0.1.5 ,打开客户端调试器发现生成的input 的name 没改过来。
//名字还是默认的file,但不是没用哦。虽然客户端名字没改变,但是提交到到后台,是要用multiFile 这个对象来取文件的,用file 是取不到文件的
// 建议作者有时间把这个地方改改啊,搞死人了。。
server: "Webuploader!ajaxAttachUpload.action",
duplicate:true,//是否可重复选择同一文件
resize: false,
formData: {
"status":"file",
"contentsDto.contentsId":"0000004730",
"uploadNum":"0000004730",
"existFlg":'false'
},
compress: null,//图片不压缩
chunked: true, //分片处理
chunkSize: 5 * 1024 * 1024, //每片5M
chunkRetry:false,//如果失败,则不重试
threads:1,//上传并发数。允许同时最大上传进程数。
// runtimeOrder: 'flash',
// 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
disableGlobalDnd: true
});
// 当有文件添加进来的时候
uploader.on( "fileQueued", function( file ) {
console.log("fileQueued:");
$list.append( "<div id='"+ file.id + "' class='item'>" +
"<h4 class='info'>" + file.name + "</h4>" +
"<p class='state'>等待上传...</p>" +
"</div>" );
});
//当所有文件上传结束时触发
uploader.on("uploadFinished",function(){
console.log("uploadFinished:");
})
//当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。
uploader.on("uploadAccept",function(object,ret){
//服务器响应了
//ret._raw 类似于 data
var data =JSON.parse(ret._raw);
if(data.resultCode != "1" && data.resultCode != "3"){
if(data.resultCode == "9"){
uploader.reset();
alert("error");
return false;
}
}else{
//E05017
uploader.reset();
alert("error");
return false;
}
})
//当文件上传成功时触发。
uploader.on( "uploadSuccess", function( file ) {
$( "#"+file.id ).find("p.state").text("已上传");
});
uploader.on( "uploadError", function( file ) {
$( "#"+file.id ).find("p.state").text("上传出错");
uploader.cancelFile(file);
uploader.removeFile(file,true);
uploader.reset();
});
$("#upload").on("click", function() {
uploader.upload();
})
});
/*********************************WebUpload 单文件上传 end*******************************************/
/************************************webuploader的自带参数提交到后台的参数列表*************************
* {
//web uploader 的自带参数
lastModifiedDate=[Wed Apr 27 2016 16:45:01 GMT+0800 (中国标准时间)],
chunks=[3], chunk=[0],
type=[audio/wav], uid=[yangl], id=[WU_FILE_0],
size=[268620636], name=[3.wav],
//formData的参数
contentsDto.contentsId=[0000004730], existFlg=[false],
status=[file], uploadNum=[0000004730]
}
*********************************************************************************************/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
6.WebuploaderAction.java 后台的实现为struts2,把分片按照上传顺序合并成一个文件。这里只是给一个实现的参考,可以根据实际项目情况来实现后台。 //属性值,单文件的情况,对应的是upload3.js中的name属性,name属性值为file,此时struts就可以获取到file的文件对象,不需要实例化,struts框架会自动注入对象值,打开调试窗口,看一下就明白了
private File file;
//单文件上传的文件名,spring上传特性,文件名格式为name属性+FileName
private String fileFileName;
//属性值,接收webupload自带的参数
private String chunk; // 当前第几个分片
private String chunks;// 总分片个数
private String size;// 单个文件的总大小
//单文件上传后台代码
public void ajaxAttachUpload() {
String path = "d:\\test\\"+fileFileName;
try {
//拿到文件对象
File file = this.getFile();
//第一个参数是目标文件的完整路径
//第二参数是webupload分片传过来的文件
//FileUtil的这个方法是把目标文件的指针,移到文件末尾,然后把分片文件追加进去,实现文件合并。简单说。就是每次最新的分片合到一个文件里面去。
FileUtil.randomAccessFile(path, file);
//如果文件小与5M的话,分片参数chunk的值是null
//5M的这个阈值是在upload3.js中的chunkSize属性决定的,超过chunkSize设置的大小才会进行分片,否则就不分片,不分片的话,webupload传到后台的chunk参数值就是null
if(StringUtils.isEmpty(chunk)){
//不分片的情况
outJson("0", "success", "");
}else{
//分片的情况
//chunk 分片索引,下标从0开始
//chunks 总分片数
if (Integer.valueOf(chunk) == (Integer.valueOf(chunks) - 1)) {
outJson("0", "上传成功", "");
} else {
outJson("2", "上传中" + fileFileName + " chunk:" + chunk, "");
}
}
} catch (Exception e) {
outJson("3", "上传失败", "");
}
}
FileUtil.java
每次把指针移到文件末尾,追加写入文件
/**
* 指定位置开始写入文件
* @param tempFile 输入文件
* @param outPath 输出文件的路径(路径+文件名)
* @throws IOException
*/
public static void randomAccessFile( String outPath,File tempFile) throws IOException{
RandomAccessFile raFile = null;
BufferedInputStream inputStream=null;
try{
File dirFile = new File(outPath);
//以读写的方式打开目标文件
raFile = new RandomAccessFile(dirFile, "rw");
raFile.seek(raFile.length());
inputStream = new BufferedInputStream(new FileInputStream(tempFile));
byte[] buf = new byte[1024];
int length = 0;
while ((length = inputStream.read(buf)) != -1) {
raFile.write(buf, 0, length);
}
}catch(Exception e){
throw new IOException(e.getMessage());
}finally{
try {
if (inputStream != null) {
inputStream.close();
}
if (raFile != null) {
raFile.close();
}
}catch(Exception e){
throw new IOException(e.getMessage());
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
7.单文件上传效果图
8.多文件上传 多文件上传webuploader是用队列控制的,文件一个一个上传。文件1在上传中的话,文件2就在排队,等文件1传完了在传文件2。这里要注意name属性和fileVal属性的结合使用,否则后台是取不到mulitFile对象的 upload3.js /*********************************WebUpload 多文件上传 begin*****************************************/
$(function(){
var $list = $("#thelist1");
var fileSize = 0; //总文件大小
var fileName = []; //文件名列表
var fileSizeOneByOne =[];//每个文件大小
var uploader ;// 实例化
uploader = WebUploader.create({
auto:false, //是否自动上传
pick: {
id: '#multi',
label: '点击选择文件',
name:"multiFile"
},
swf: '../../main/js/Uploader.swf',
fileVal:'multiFile', //和name属性配合使用
server: "Webuploader!ajaxAttachUpload2.action",
duplicate:true, //同一文件是否可重复选择
resize: false,
formData: {
"status":"multi",
"contentsDto.contentsId":"0000004730",
"uploadNum":"0000004730",
"existFlg":'false'
},
compress: null,//图片不压缩
chunked: true, //分片
chunkSize: 5 * 1024 * 1024, //每片5M
chunkRetry:false,//如果失败,则不重试
threads:1,//上传并发数。允许同时最大上传进程数。
//fileNumLimit:50,//验证文件总数量, 超出则不允许加入队列
// runtimeOrder: 'flash',
// 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
disableGlobalDnd: true
});
// 当有文件添加进来的时候
uploader.on( "fileQueued", function( file ) {
console.log("fileQueued:");
$list.append( "<div id='"+ file.id + "' class='item'>" +
"<h4 class='info'>" + file.name + "</h4>" +
"<p class='state'>等待上传...</p>" +
"</div>" );
});
// 当开始上传流程时触发
uploader.on( "startUpload", function() {
console.log("startUpload");
//添加额外的表单参数
$.extend( true, uploader.options.formData, {"fileSize":fileSize,"multiFileName":fileName,"fileSizeOneByOne":fileSizeOneByOne});
});
//当某个文件上传到服务端响应后,会派送此事件来询问服务端响应是否有效。
uploader.on("uploadAccept",function(object,ret){
//服务器响应了
//ret._raw 类似于 data
console.log("uploadAccept");
console.log(ret);
var data =JSON.parse(ret._raw);
if(data.resultCode!="1" && data.resultCode !="3"){
if(data.resultCode == "9"){
alert("error");
uploader.reset();
return;
}
}else{
uploader.reset();
alert("error");
}
})
uploader.on( "uploadSuccess", function( file ) {
$( "#"+file.id ).find("p.state").text("已上传");
});
//出错之后要把文件从队列中remove调,否则,文件还在队里中,还是会上传到后台去
uploader.on( "uploadError", function( file,reason ) {
$( "#"+file.id ).find("p.state").text("上传出错");
console.log("uploadError");
console.log(file);
console.log(reason);
//多个文件
var fileArray = uploader.getFiles();
for(var i = 0 ;i<fileArray.length;i++){
//取消文件上传
uploader.cancelFile(fileArray[i]);
//从队列中移除掉
uploader.removeFile(fileArray[i],true);
}
//发生错误重置webupload,初始化变量
uploader.reset();
fileSize = 0;
fileName = [];
fileSizeOneByOne=[];
});
//当validate不通过时,会以派送错误事件的形式通知调用者
uploader.on("error",function(){
console.log("error");
uploader.reset();
fileSize = 0;
fileName = [];
fileSizeOneByOne=[];
alert("error");
})
//如果是在模态框里的上传按钮,点击file的时候不会触发控件
//修复model内部点击不会触发选择文件的BUG
/* $("#multi .webuploader-pick").click(function () {
uploader.reset();
fileSize = 0;
fileName = [];
fileSizeOneByOne=[];
$("#multi :file").click();
});*/
/**
* 多文件上传
*/
$("#multiUpload").on("click",function(){
uploader.upload();
})
/**
*取得每个文件的文件名和文件大小
*/
//选择文件之后执行上传
$(document).on("change","input[name='multiFile']", function() {
//multiFileName
var fileArray1 = uploader.getFiles();
var fileNames = [];
for(var i = 0 ;i<fileArray1.length;i++){
fileNames.push(fileArray1[i].name); //input 框用
//后台用
fileSize +=fileArray1[i].size;
fileSizeOneByOne.push(fileArray1[i].size);
fileName.push(fileArray1[i].name);
}
console.log(fileSize);
console.log(fileSizeOneByOne);
console.log(fileName);
})
});
/*********************************WebUpload 多文件上传 end*****************************************/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
9.多文件后台代码,WebuploaderAction.java //多文件上传的文件对象,多文件是一个一个文件上传,所以multiFile是当前正在上传的文件对象
private File multiFile;
//多文件上传文件对象的文名,当前正在上传的文件名
private String multiFileFileName;
//属性值,接收webupload自带的参数
private String chunk; // 当前第几个分片
private String chunks;// 总分片个数
private String size;// 单个文件的总大小
//自定义属性值
private String fileSize;//所有文件的总大小
private String[] multiFileName ;// 文件名列表
private String[] fileSizeOneByOne;//每个文件大小
private String status;
/***
* 多文件上传的核心是,前端的文件队列里面,文件一个一个排着队,等第一个文件上传完了,在上传第二个文件,
* 前端反复多次调用这个方法,mulitiFIleFileName为当前正在上传的文件名
*/
public void ajaxAttachUpload2() {
String path = "d:\\test\\"+multiFileFileName;
/**
* TODO:可添加自己的业务逻辑实现
* fileSize;//所有文件的总大小
* multiFileName ;// 文件名列表
* fileSizeOneByOne;//每个文件大小
*
* 后台已经可以拿到这些属性,可以根据每个文件的大小,和总大小计算出 上传的进度百分比等。
* 或者可以 判断文件上传的大小是否正确。是否丢字节。
*/
try {
//拿到当前正在上传的文件对象
File file = this.getMultiFile();
FileUtil.randomAccessFile(path, file);
if(StringUtils.isEmpty(chunk)){
//不分片的情况
outJson("0", "success", "");
}else{
//分片的情况
//chunk 分片索引,下标从0开始
//chunks 总分片数
if (Integer.valueOf(chunk) == (Integer.valueOf(chunks) - 1)) {
outJson("0", "上传成功", "");
} else {
outJson("2", "上传中" + fileFileName + " chunk:" + chunk, "");
}
}
} catch (Exception e) {
outJson("3", "上传失败", "");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
10.多文件上传效果图
参考用,放一下源代码的下载链接,代码只做参考用,持续更新中… 在我的个人资源里面有,链接点不了的,可以直接在个人资源里面找一下。找那个完整版的。 Webupload大文件分片上传源码完整版 PS:想说的话 评论区和下载区的留言我都有在看。。。还有人在问怎么传自定义的值到后台,大哥,耐着性子把上面的例子好好看看成吗?例子里都有写,告诉你怎么传了。。。下载区的demo是我真实试验过,可以跑起来的代码,但毕竟只能是参考。程序员不调试,不碰几个错,别人在怎么给你写清楚,也终究是学不到东西。纸上得来终觉浅,绝知此事要躬行。 |