一年前的一个项目,有人反馈上传超时,超时原因是文件大小超出限制。由于原来维护项目的人员离开,现在上传超时的问题就有我来处理。解决方案也比较简单,就是切片上传。前端不想自己写了,就利用了上传组件,这个上传组件是百度
的WebUploader
。WebUploader
这个组件被使用的也比较广泛,为了省事就用它啦!
上传中
合并后
代码实现
前端代码
前端上传以及分片使用的百度上传组件WebUploader
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Home Page - WebUpload</title>
<link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" href="/">WebUpload</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" href="/Home/Privacy">Privacy</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
<link rel="stylesheet" type="text/css" href="/webuploader/webuploader.css">
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script src="/webuploader/webuploader.js"></script>
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<div id="uploader" class="wu-example">
<div class="container-fluid">
<div class="col-md-10">
<div class="row">文件上传示例:</div>
<div class="row">
<div id="uploader" class="wu-example">
<!--用来存放文件信息-->
<div id="fileList" class="uploader-list"></div>
<div class="btns">
<div id="picker" class="btn btn-primary">选择文件</div>
</div>
</div>
</div>
<div class="row">
</div>
<div class="row"> <button id="ctlBtn" class="btn btn-default">开始上传</button></div>
</div>
<div>
</div>
</div>
</div>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<script>
jQuery(function () {
var $ = jQuery,
$list = $('#fileList'),
$btn = $('#ctlBtn'),
state = 'pending',
uploader, guid = {};
uploader = WebUploader.create({
// 不压缩image
resize: false,
// swf文件路径
swf: "~/webuploader/Uploader.swf",
server: 'http://localhost:8011/api/File/UplaodFile?isConvert=true',
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: '#picker',
chunked: true
});
// 当有文件添加进来的时候
uploader.on('fileQueued', function (file) {
guid[file.id] = +(new Date());
$list.append('<div id="' + file.id + '" class="item">' +
'<h4 class="info">' + file.name + '</h4>' +
'<p class="state">等待上传...</p>' +
'</div>');
uploader
.md5File(file)
.progress(function (percentage) {
// console.log("Percentage:", percentage);
$("#ctlBtn").hide();
})
// MD5计算完毕,可以点击上传了
.then(function (fileMd5) {
file.fileMd5 = fileMd5;
$("#ctlBtn").show()
console.log(fileMd5);
});
});
uploader.on('uploadBeforeSend', function (file, data, headers) {
$.extend(data, { "fileId": guid[data.id], "fileMd5": file.file.fileMd5 });
$.extend(headers, {
"Authorization":"Bearer "
});
if (data.chunks) {
data["chunkStart"] = file.start;
data["chunkEnd"] = file.end;
}
});
// 文件上传过程中创建进度条实时显示。
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) {
$('#' + 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();
});
uploader.on('all', function (type) {
if (type === 'startUpload') {
state = 'uploading';
} else if (type === 'stopUpload') {
state = 'paused';
} else if (type === 'uploadFinished') {
state = 'done';
}
if (state === 'uploading') {
$btn.text('暂停上传');
} else {
$btn.text('开始上传');
}
});
$btn.on('click', function () {
if (state === 'uploading') {
uploader.stop();
} else {
uploader.upload();
}
});
});
</script>
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2020 - WebUpload - <a href="/Home/Privacy">Privacy</a>
</div>
</footer>
<script src="/lib/jquery/dist/jquery.js"></script>
<script src="/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
<script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>
</body>
</html>
后台实现
后台使用的是ASP .NET Core Web API,实现方式也很简单。文件的校验,分块的校验等这里就省略了。
[Route("api/[controller]")]
public class FileController : Controller
{
public static readonly object locker = new object();
[HttpPost]
[Route("UplaodFile")]
public IActionResult UplaodFile([FromForm] IFormCollection form)
{
try
{
string fileId = form["fileId"];
if (string.IsNullOrWhiteSpace(fileId)||form.Files==null||form.Files.Count==0)
{
return BadRequest();
}
string tempFileFolder = Path.Combine(AppContext.BaseDirectory, $"FileUploader/{fileId}");
CreateFolder(tempFileFolder);
if (form.ContainsKey("chunks"))
{
string chunksString = form["chunks"];
int chunks = int.Parse(chunksString);
string chunkString = form["chunk"];
int chunk = int.Parse(chunkString);
string sizeString = form["size"];
long size = long.Parse(sizeString);
string chunkStartString = form["chunkStart"];
string chunkEndString = form["chunkEnd"];
string chunkMd5 = form["chunkMd5"];
string fileMd5 = form["fileMd5"];
string lastModifiedDate = form["lastModifiedDate"];
var file = form.Files.FirstOrDefault();
string name = file.FileName;
string ext = Path.GetExtension(name);
Stream stream = file.OpenReadStream();
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
string factFilePath = Path.Combine(tempFileFolder, $"{name}");
string chunkFilePath = $"{factFilePath}.chunk{chunk}";
string chunkFileTempPath = $"{chunkFilePath}.temp";
System.IO.File.Delete(chunkFileTempPath);
FileStream fs = new FileStream(chunkFileTempPath, FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(bytes);
bw.Flush();
bw.Close();
bw.Dispose();
fs.Close();
fs.Dispose();
stream.Close();
stream.Dispose();
System.IO.File.Move(chunkFileTempPath, chunkFilePath);
bool isMerge = true;
for (int i = 0; i < chunks; i++)
{
if (!System.IO.File.Exists($"{factFilePath}.chunk{i}"))
{
isMerge = false;
break;
}
}
if (isMerge)
{
lock (locker)
{
if (isMerge)
{
if (System.IO.File.Exists(factFilePath))
{
return Ok();
}
var fileStream = new FileStream(factFilePath, FileMode.Create);
for (int i = 0; i < chunks; i++)
{
string chunkFile = $"{factFilePath}.chunk{i}";
var chunkBytes = System.IO.File.ReadAllBytes(chunkFile);
fileStream.Write(chunkBytes, 0, chunkBytes.Length);
fileStream.Flush();
System.IO.File.Delete(chunkFile);//删除分块
}
fileStream.Close();
fileStream.Dispose();
}
}
}
}
else
{
var file = form.Files.FirstOrDefault();
Stream stream = file.OpenReadStream();
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
FileStream fileStream = new FileStream(Path.Combine(tempFileFolder, $"{file.FileName}"), FileMode.Create);
BinaryWriter bw = new BinaryWriter(fileStream);
bw.Write(bytes);
bw.Flush();
bw.Close();
bw.Dispose();
fileStream.Close();
fileStream.Dispose();
stream.Close();
stream.Dispose();
}
}
catch (Exception ex)
{
Console.Write(ex);
}
return Ok();
}
private void CreateFolder(string folderPath)
{
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
}
[HttpGet]
[Route("Index")]
public IActionResult Index()
{
return Ok();
}
}
示例下载
.NET CORE大文件分/切片上传示例
转载请注明:清风亦平凡 » ASP.NET Core Web Api实现大文件切/分片上传