1,是基于webUploader的前端开源插件实现的大大文件 分片上传功能:四种文件上传格式(以后要改进的地方)普通按钮点击上传
拖拽上传
复制粘贴上传
拖拽+按钮+复制粘贴上传
多线程上传文件
2,简单的文件分片合并上传原理:
借鉴的博客: 地址
前段页面
分片上传6
//方式6
$(".btnFile6").click(function () {
var upload = function (file, skip) {
var formData = new FormData();//初始化一个FormData对象
var blockSize = 1000000;//每块的大小
var nextSize = Math.min((skip + 1) * blockSize, file.size);//读取到结束位置
var fileData = file.slice(skip * blockSize, nextSize);//截取 部分文件 块
formData.append("file", fileData);//将 部分文件 塞入FormData
formData.append("fileName", file.name);//保存文件名字
$.ajax({
url: "/Home/SaveFile6",
type: "POST",
data: formData,
processData: false, // 告诉jQuery不要去处理发送的数据
contentType: false, // 告诉jQuery不要去设置Content-Type请求头
success: function (responseText) {
$(".result").html("已经上传了" + (skip + 1) + "块文件");
if (file.size <= nextSize) {//如果上传完成,则跳出继续上传
alert("上传完成");
return;
}
upload(file, ++skip);//递归调用
}
});
};
var file = $("#file6")[0].files[0];
upload(file, 0);
});
后端代码public string SaveFile6()
{
//保存文件到根目录 App_Data + 获取文件名称和格式
var filePath = Server.MapPath("~/App_Data/") + Request.Form["fileName"];
//创建一个追加(FileMode.Append)方式的文件流
using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
using (BinaryWriter bw = new BinaryWriter(fs))
{
//读取文件流
BinaryReader br = new BinaryReader(Request.Files[0].InputStream);
//将文件留转成字节数组
byte[] bytes = br.ReadBytes((int)Request.Files[0].InputStream.Length);
//将字节数组追加到文件
bw.Write(bytes);
}
}
return "保存成功";
}
3,监控文件上传的三个时间点:(上传)本地项目实例时间点1: 所有分块进行上传之前(1,算文件的唯一标识,2,判断文件是否秒传)计算分片文件的MD5唯一标识,
请求后台是否保存过该文件,存在跳过该文件,不存在则继续上传.
时间点2: 如果分片上传, 每个分片上传之前(1, 通知询问后台是否已经保存成功, 用于断点续传)每个每个分块有每个分块的下标和大小.请求后台是否保存过当前分块,存在跳过该分片文件,实现断点续传
请求后台是否保存完成该文件信息,如果保存过,则跳过; 如果不存在或者不完整则发送该分块内容
携带当前文件的唯一标识到后台, 用于让后台创建保存该文件分块的目录
时间点3: 所有分块上传成功之后(1, 通知后台进行分块文件的合并工作)前台通知后台合并文件
3,后台文件的处理:FileChannel outChannel = FileOutputStream(file).getChannel()FileChannel inChannel(File tmp : fileList) {
inChannel = FileInputStream(tmp).getChannel()inChannel.transferTo(inChannel.size()outChannel)inChannel.close()file.delete()}
(outChannel != ) {
(f)outChannel.close()}
文件上传的前端代码:
webuploaderDemo.upload-table{
padding: 2px 12px;
}
.fileQueue{
height: 300px;
overflow-x: hidden;
overflow-y: auto;
background-color: #f8f8f8;
width: 100%;
}
.hiden{
display: none
}
var base = "/upload";
var element;
var layer;
var index=0;
var batchWebUpload;
var fileAllNum=0;
var fileAllSize=0;
var successNum=0;
var successSize=0;
var percentages = {}; // 所有文件的进度信息,key为file id
$(function(){
layui.use(['layer','element','code'], function(){
element = layui.element;
layer = layui.layer;
layui.code({
elem: 'pre', //默认值为.layui-code
encode: true, //是否转义html标签。默认不开启
about: false
});
layer.ready(function(){
showWindow()
});
});
var fileCheckUrl=base+"/file/checkFile";//检测文件是否存在url
var checkChunkUrl=base+"/file/checkChunk";//检测分片url
var mergeChunksUrl=base+"/file/mergeChunks";//合并文件请求地址
//监控文件上传的三个时间点(注意:该段代码必须放在WebUploader.create之前)
//时间点1::所有分块进行上传之前(1.可以计算文件的唯一标记;2.可以判断是否秒传)
//时间点2: 如果分块上传,每个分块上传之前(1.询问后台该分块是否已经保存成功,用于断点续传)
//时间点3:所有分块上传成功之后(1.通知后台进行分块文件的合并工作)
WebUploader.Uploader.register({
"before-send-file":"beforeSendFile",
"before-send":"beforeSend",
"after-send-file":"afterSendFile"
},{
//时间点1::所有分块进行上传之前调用此函数
//时间点1::所有分块进行上传之前调用此函数
beforeSendFile:function(file){//利用md5File()方法计算文件的唯一标记符
//创建一个deffered
var deferred = WebUploader.Deferred();
//1.计算文件的唯一标记,用于断点续传和秒传,获取文件前2m的md5值,越小越快,防止碰撞,把文件名文件大小和md5拼接作为文件唯一标识
(new WebUploader.Uploader()).md5File(file,0,2*1024*1024).progress(function(percentage){
}).then(function(val){
fileMd5 = file.size+"-"+val+"-"+file.name;//防止碰撞,把文件名文件大小和md5拼接作为文件唯一标识
file.fileMd5=fileMd5;
//2.请求后台是否保存过该文件,如果存在,则跳过该文件,实现秒传功能
$.ajax({
type:"POST",
url:fileCheckUrl,
data:{
fileMd5:fileMd5//文件唯一标记
},
dataType:"json",
success:function(response){
console.log(response);
if(response.success){
console.log($("#"+file.id).find('.percent').html());
$("#"+file.id).find('.percent').html("100%");
$("#"+file.id).find(".layui-progress-bar").removeClass('layui-bg-blue');
element.progress('progress_'+file.id, '100%');
batchWebUpload.skipFile(file);
successNum++;
successSize+=file.size;
//如果存在,则跳过该文件,秒传成功
deferred.reject();
}else{
//继续上传
deferred.resolve();
}
}
}
);
});
//返回deffered
return deferred.promise();
},
//时间点2:如果有分块上传,则 每个分块上传之前调用此函数
//block:代表当前分块对象
beforeSend:function(block){//向后台发送当前文件的唯一标记,用于后台创建保存分块文件的目录
//1.请求后台是否保存过当前分块,如果存在,则跳过该分块文件,实现断点续传功能
var deferred = WebUploader.Deferred();
//请求后台是否保存完成该文件信息,如果保存过,则跳过,如果没有,则发送该分块内容
$.ajax({
type:"POST",
url:checkChunkUrl,
data:{
//文件唯一标记,敲黑板划重点了,不要用file.fileMd5,不然服务器上的文件会出错
fileMd5: block.file.fileMd5,
//当前分块下标
chunk:block.chunk,
//当前分块大小
chunkSize:block.end-block.start
},
dataType:"json",
success:function(response){
if(response.success){
//分块存在,跳过该分块
deferred.reject();
}else{
//分块不存在或者不完整,重新发送该分块内容
deferred.resolve();
}
}
}
);
//携带当前文件的唯一标记到后台,用于让后台创建保存该文件分块的目录
// this.owner.options.formData.fileMd5 = block.file.fileMd5;
return deferred.promise();
},
//时间点3:所有分块上传成功之后调用此函数
afterSendFile:function(file){//前台通知后台合并文件
//1.如果分块上传,则通过后台合并所有分块文件
//请求后台合并文件
$.ajax({
type:"POST",
url:mergeChunksUrl,
data:{
//文件唯一标记
fileMd5:file.fileMd5,
//文件名称
fileName:file.name
},
dataType:"json",
success:function(response){
console.log("合并分片完成");
console.log(response);
$("#"+file.id).find(".layui-progress-bar").removeClass('layui-bg-blue');
element.progress('progress_'+file.id, '100%');
$("#"+file.id).find('.percent').html("100%");
successNum++;
successSize+=file.size;
}
});
}
});
var batchWebUpload_btn = $("#batchWebUpload");
batchWebUpload = WebUploader.create({
auto:false,
pick: {
id: batchWebUpload_btn,//指定选择文件的按钮容器,不指定则不创建按钮。注意 这里虽然写的是 id, 不仅支持 id, 还支持 class, 或者 dom 节点。
//label : title, 官方建议采用 innerHTML 代替
//innerHTML : title,
multiple :true //开启文件多选
},
flash:base+'/staticresource/webuploader/Uploader.swf',//ie9一下会自动使用flash上传
/** accept:{//不验证文件类型了
title: '不验证了',//字符串类型,文字描述
extensions: '*',//允许的文件后缀,不带点,多个用逗号分割。
mimeTypes: 'application/*,'//多个用逗号分割,怎么不知道咋写的,参考w3c MIME 参考手册,传送门 http://www.w3school.com.cn/media/media_mimeref.asp
},**/
server: base+"/file/uploadChunks",
//压缩图片,如果图片尺寸超过设置的尺寸,会自动压缩图片,必要时会裁剪
compress:{
width: 600,
height: 600,
// 图片质量,只有type为`image/jpeg`的时候才有效。
quality: 90,
// 是否允许放大,如果想要生成小图的时候不失真,此选项应该设置为false.
allowMagnify: false,
// 是否允许裁剪
crop: false,
// 是否保留头部meta信息。
preserveHeaders: true,
// 如果发现压缩后文件大小比原来还大,则使用原来图片
// 此属性可能会影响图片自动纠正功能
noCompressIfLarger: false
},
// 单位字节,如果图片大小小于此值,不会采用压缩。512k 512*1024,如果设置为0,原图尺寸大于设置的尺寸就会压缩;如果大于0,只有在原图尺寸大于设置的尺寸,并且图片大小大于此值,才会压缩
compressSize: 0,
fileNumLimit : 10,//验证文件总数量, 超出则不允许加入队列,默认值:undefined,如果不配置,则不限制数量
fileSizeLimit : 100*1024*1024*1024, //1kb=1024*1024,验证文件总大小是否超出限制, 超出则不允许加入队列。
fileSingleSizeLimit :10*1024*1024*1024, //验证单个文件大小是否超出限制, 超出则不允许加入队列。
chunked:true,//是否开启分片上传
threads:3,
chunkSize:5*1024*1024,//如果要分片,每一片的文件大小
prepareNextFile:false//在上传当前文件时,准备好下一个文件,请设置成false,不然开启文件多选你浏览器会卡死
});
//当文件上传成功时触发。file {File} File对象, response {Object}服务端返回的数据
batchWebUpload.on('uploadSuccess',function(file,response){
layer.msg("上传完成,服务端返回信息请按F12,看控制台:");
console.log(file);
if(response.success){
console.log(response);
}
})
//错误类型。文件验证不通过时触发
//错误类型说明:Q_EXCEED_NUM_LIMIT 上传文件超过限制的数量
//Q_EXCEED_SIZE_LIMIT文件总大小超出限制
//Q_TYPE_DENIED 当文件类型不对
batchWebUpload.on("error",function(type,file){
console.log(type);
if (type=="Q_TYPE_DENIED"){
layer.msg("只能上传gif,jpg,jpeg,bmp,png格式文件");
}else if(type=="Q_EXCEED_SIZE_LIMIT"){
layer.msg("所有的文件大小总和不能超过10M");
}else if(type=='F_EXCEED_SIZE'){
layer.msg("单个文件大小不能超过1M");
}else if(type=='Q_EXCEED_NUM_LIMIT'){
layer.msg(typeName+"最多只能上传10个");
}else if(type=='F_DUPLICATE'){
layer.msg(file.name+"已经在上传队列,请勿重复上传");
}else{
layer.msg("上传出错");
}
})
//文件加入队列
batchWebUpload.on("fileQueued",function(file){
fileAllNum++;
fileAllSize+=file.size;
var fileSize = (file.size/1024/1024.0).toFixed(2)+"M";
var progress = '
var buttons = '暂停';
buttons+='
';buttons+='删除';
var htm = '
'+file.name+'
'+file.ext+'
'+fileSize+'
'+progress+ '0%等待上传'+buttons+'';$("#fileList").append(htm);
//绑定事件
$("#pause_Btn_"+file.id).bind('click',function(){
batchWebUpload.stop(file);
})
$("#continue_Btn_"+file.id).bind('click',function(){
batchWebUpload.upload(file);
})
$("#delete_Btn_"+file.id).bind('click',function(){
batchWebUpload.removeFile( file, true );//从文件队列移除
})
percentages[ file.id ] = [ file.size, 0 ];
file.on('statuschange', function( cur, prev ) {
// 成功
if ( cur === 'error' || cur === 'invalid' ) {
percentages[ file.id ][ 1 ] = 1;
} else if ( cur === 'queued' ) {
percentages[ file.id ][ 1 ] = 0;
}
})
$("#"+file.id).find('.percent').html("0%");
element.progress('progress_'+file.id, '0%');
});
/**从文件队列移除**/
batchWebUpload.on('fileDequeued', function( file ) {
fileAllNum--;
fileAllSize-=file.size;
delete percentages[ file.id ];
});
/**上传之前**/
batchWebUpload.on('uploadBeforeSend', function( block, data, headers ) {
data.fileMd5 = block.file.fileMd5;
//block.file.chunks = block.chunks;//当前文件总分片数量
data.chunks = block.file.chunks;
});
/**上传过程中触发,携带上传进度**/
batchWebUpload.on('uploadProgress',function(file ,percentage){
var percent=(percentage*100 ).toFixed(2)+"%";
element.progress('progress_'+file.id, percent);//设置进度条百分比
if(JSON.stringify(percentages) != "{}"){
percentages[ file.id ][ 1 ] = percentage;
}
if(percentage==1){
percentages[ file.id ][ 1 ] = 1;
}
$("#"+file.id).find(".status").text("正在上传");
$("#"+file.id).find('.percent').html(percent);
})
batchWebUpload.on( 'all', function( type ,file) {
updateTotalProgress();
switch( type ) {
case 'uploadComplete':
parentId = file.id;
percentages[ file.id ][ 1 ] = 1;
break;
case 'uploadFinished':
break;
case 'uploadProgress':
break;
case 'uploadStart':
break;
case 'stopUpload':
break;
}
})
})
/**左下角弹出框**/
function showWindow(){
if(index!=0){
try{
layer.restore(index);
}catch(err){
//console.log("弹出层已经打开");
return index;
}
return index;
}
//iframe窗
index=layer.open({
type: 1,
title: ['上传队列', 'font-size:14px;'],
closeBtn: 0, //不显示关闭按钮
shade: false,
area: ['700px', '400px'],
offset: 'rb', //右下角弹出
anim: 2,
maxmin :true,
content: $("#fileListDom"), //iframe的url,no代表不显示滚动条
});
return index;
}
function startAll(file){
batchWebUpload.upload(file);
}
function stopUpload(file){
if(file==undefined){
batchWebUpload.stop();
}else{
batchWebUpload.stop(file);
}
}
function cancelFile(file){
batchWebUpload.cancelFile(file);
}
/**设置总百分比**/
function updateTotalProgress() {
var fize = (fileAllSize/1024/1024.0).toFixed(2);
var sSize = (successSize/1024/1024.0).toFixed(2);
$("#fileQueueNum").text(fileAllNum)
$("#fileQueueSize").text(fize);
var loaded = 0;
var total = 0;
var percent = 0;;
$.each( percentages, function( k, v ) {
total += v[ 0 ];
loaded += v[ 0 ] * v[ 1 ];
} );
percent = total ? loaded / total : 0;
$("#successNum").text(successNum);
$("#successSize").text(sSize);
var show_pe= Math.round( percent * 100 ) + '%';
element.progress('sumProgress', show_pe);
$("#sumPercent").text(show_pe);
}
划重点,多选,分片批量上传+断点续传
后端代码