有你在真好 的个人博客
NetCore+WebUploader实现大文件分片上传
阅读:2028 添加日期:3/30/2021 5:41:48 PM

项目要求通过网站上传大文件,比如视频文件,通过摸索实现了文件分片来上传,然后后台进行合并。

使用了开源的前台上传插件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