.NetCore+WebUploader实现大文件分片上传
程序员文章站
2022-05-06 21:51:28
项目要求通过网站上传大文件,比如视频文件,通过摸索实现了文件分片来上传,然后后台进行合并。 使用了开源的前台上传插件WebUploader(http://fex.baidu.com/webuploader/) WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5 ......
项目要求通过网站上传大文件,比如视频文件,通过摸索实现了文件分片来上传,然后后台进行合并。
使用了开源的前台上传插件webuploader(http://fex.baidu.com/webuploader/)
webuploader是由baidu webfe(fex)团队开发的一个简单的以html5为主,flash为辅的现代文件上传组件。在现代的浏览器里面能充分发挥html5的优势,同时又不摒弃主流ie浏览器,沿用原来的flash运行时,兼容ie6+,ios 6+, android 4+。两套运行时,同样的调用方式,可供用户任意选用。
采用大文件分片并发上传,极大的提高了文件上传效率。
直接上代码,前台cshtml
@{
layout = null;
}
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<link href="~/js/webuploader/webuploader.css" rel="stylesheet" />
<link href="~/js/bootstrap.min.css" rel="stylesheet" />
<script src="~/js/jquery.min.js"></script>
<script src="~/js/webuploader/webuploader.js"></script>
<title>upload</title>
<script>
jquery(function () {
var $ = jquery,
$list = $('#thelist'),
$btn = $('#ctlbtn'),
state = 'pending',
filemd5,
flag = true,
datastate,
fm = [],
fnum,
token,
uploader;
var fileext = ["mpg", "mpeg", "mp4", "avi"];
token = '@viewbag.token';
if (token == '' || token== 'undefined')
{
$("#uploader").hide();
alert("登录超时,请重新登录。");
}
//监听分块上传过程中的三个时间点
webuploader.uploader.register({
"before-send-file": "beforesendfile",
"before-send": "beforesend",
"after-send-file": "aftersendfile",
}, {
beforesendfile: function (file) {
var starttime = new date(file.lastmodifieddate);
filename = file.name;
var deferred = webuploader.deferred();
(new webuploader.uploader()).md5file(file, 0, 10 * 1024 * 1024)
.progress(function (percentage) {
console.log("正在读取文件");
})
.then(function (val) {
filemd5 = val;
fm.push(filemd5);
deferred.resolve();
});
return deferred.promise();
},
//时间点2:如果有分块上传,则每个分块上传之前调用此函数
beforesend: function (block) {
var deferred = webuploader.deferred();
//上传前ajax检测一下此文件块是否已经上传
this.owner.options.formdata.filemd5 = filemd5;
this.owner.options.formdata.chunk = block.chunk;
deferred.resolve();
return deferred.promise();
},
//时间点3:所有分块上传成功后调用此函数
aftersendfile: function (file) {
var deferred = $.deferred();
$('#' + file.id).find('p.state').text('执行最后一步');
console.log(file);
console.log(file.guid);
$.ajax({
type: "post",
url: "/api/v1/check/filemerge",
data: {
guid: file.guid,
filemd5: fm[fnum],
filename: file.name
},
cache: false,
async: false,
success: function (response) {
fnum++;
console.log(response);
if (response.success == true) {
datastate = response;
flag = true;
} else {
flag = false;
}
deferred.resolve();
},
error: function () {
fnum++;
datastate = undefined;
flag = false;
deferred.reject();
}
});
return deferred.promise();
}
});
uploader = webuploader.create({
resize: false,
filenumlimit: 10,
swf: '/js/uploader.swf',
server: '/api/v1/check/filesave',
pick: '#picker',
chunked: true,
chunksize: 10 * 1024 * 1024,
chunkretry: 5
//, formdata: {
// guid: guid
//}
});
uploader.on('beforefilequeued', function (file) {
var isadd = false;
for (var i = 0; i < fileext.length; i++) {
if (file.ext == fileext[i]) {
file.guid = webuploader.base.guid();
isadd = true;
break;
}
}
return isadd;
});
uploader.on('uploadbeforesend', function (object, data, headers) {
//console.log(object);
headers.authorization =token;
data.guid = object.file.guid;
});
// 当有文件添加进来的时候
uploader.on('filequeued', function (file) {
$list.append('<div id="' + file.id + '" class="item">' +
'<h4 class="info">' + file.name + '</h4>' +
'<input type="hidden" id="h_' + file.id + '" value="' + file.guid + '" />' +
'<p class="state">等待上传...</p>' +
'</div>');
});
// 文件上传过程中创建进度条实时显示。
uploader.on('uploadprogress', function (file, percentage) {
var $li = $('#' + file.id),
$percent = $li.find('.progress .progress-bar');
// 避免重复创建
if (!$percent.length) {
$percent = $('<div class="progress progress-striped active">' +
'<div class="progress-bar" role="progressbar" style="width: 0%">' +
'</div>' +
'</div>').appendto($li).find('.progress-bar');
}
$li.find('p.state').text('上传中');
$percent.css('width', percentage * 100 + '%');
});
uploader.on('uploadsuccess', function (file) {
if (datastate == undefined) {
$('#' + file.id).find('p.state').text('上传失败');
$('#' + file.id).find('button').remove();
$('#' + file.id).find('p.state').before('<button id="retry" type="button" class="btn btn-primary fright retry pbtn">重新上传</button>');
flag = false;
file.setstatus('error');
}
if (datastate.success == true) {
$('#' + file.id).find('p.state').text('已上传');
$('#' + file.id).find('button').remove();
} else {
$('#' + file.id).find('p.state').text('上传失败');
flag = false;
}
});
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 === 'done') {
$btn.text('继续上传');
} else if (state === 'uploading') {
$btn.text('暂停上传');
} else {
$btn.text('开始上传');
}
});
$btn.on('click', function () {
if (state === 'uploading') {
uploader.stop();
} else if (state == 'done') {
window.location.reload();
}
else {
uploader.upload();
}
});
});
</script>
</head>
<body>
<div id="uploader" class="wu-example">
<span style="color:red">只能上传mpg、mpeg、mp4、avi格式的视频文件</span>
<div id="thelist" class="uploader-list"></div>
<div class="btns">
<div id="picker" class="webuploader-container"><div class="webuploader-pick">选择文件</div><div style="position: absolute; top: 0px; left: 0px; width: 88px; height: 34px; overflow: hidden; bottom: auto; right: auto;"><input type="file" name="file" class="webuploader-element-invisible" multiple="multiple"><label style="opacity: 0; width: 100%; height: 100%; display: block; cursor: pointer; background: rgb(255, 255, 255);"></label></div></div>
<button id="ctlbtn" class="btn btn-default">开始上传</button>
</div>
</div>
</body>
</html>
后台代码:
#region 上传视频
public iactionresult upload()
{
viewbag.token = httpcontext.request.headers["authorization"];//获取认证信息,传递给前台,方便ajax请求时提供
return view();
}
/// <summary>
/// 上传文件
/// </summary>
/// <returns></returns>
[httppost]
public async task<iactionresult> filesave()
{
var date = request;
var files = request.form.files;
long size = files.sum(f => f.length);
foreach (var formfile in files)
{
if (formfile.length > 0)
{
string fileext = formfile.filename.substring(formfile.filename.indexof('.')); //文件扩展名,不含“.”
long filesize = formfile.length; //获得文件大小,以字节为单位
//string newfilename = guid.newguid().tostring() + "." + fileext; //随机生成新的文件名
string dirpath = path.combine(_uploadconfig.tmppath, request.form["guid"]);
if (!directory.exists(dirpath))
{
directory.createdirectory(dirpath);
}
var filepath = dirpath + "/" + request.form["chunk"] + fileext;
using (var stream = new filestream(filepath, filemode.create))
{
await formfile.copytoasync(stream);
}
}
}
return ok(new { count = files.count, size });
}
/// <summary>
/// 合并请求
/// </summary>
/// <returns></returns>
[httppost]
public async task<iactionresult> filemerge()
{
bool ok = false;
string errmsg = "";
try
{
var temporary = path.combine(_uploadconfig.tmppath, request.form["guid"]);//临时文件夹
string filename = request.form["filename"];//文件名
string fileext = path.getextension(filename);//获取文件后缀
var files = directory.getfiles(temporary);//获得下面的所有文件
var finalfilepath = path.combine(_uploadconfig.uploadpath + filename);//最终的文件名
//var fs = new filestream(finalfilepath, filemode.create);
using (var fs = new filestream(finalfilepath, filemode.create))
{
foreach (var part in files.orderby(x => x.length).thenby(x => x))
{
var bytes = system.io.file.readallbytes(part);
await fs.writeasync(bytes, 0, bytes.length);
bytes = null;
system.io.file.delete(part);//删除分块
}
directory.delete(temporary);//删除文件夹
ok = true;
}
}
catch (exception ex)
{
ok = false;
errmsg = ex.message;
log4net.error(errmsg);
}
if (ok)
{
return ok(new { success = true, msg = "" });
}
else
{
return ok(new { success = false, msg = errmsg }); ;
}
}
#endregion