.NET 7.0或.NET Core Web APi基于tus协议实现断点续传上传大文件
.NET 7.0或.NET Core Web APi基于tus协议实现断点续传上传大文件
【导读】前几天由于看到网上的断点续传的文章,也不能说是同出一辙,那简直一模一样,于是有了此文章,不会的童鞋可以上GIthub上查看DEMO。
-
基于tus协议实现断点续传演示
- 基于tus协议前端脚本
关于此协议实现原理这里不做阐述,请参照上述github地址自行了解,本文只给出相关的代码,仅供参考!
<link href="~/lib/layui/css/layui.css" rel="stylesheet"/>
<script src="~/lib/layui/layui.min.js"></script>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/tus-js-client/dist/tus.min.js"></script>
<div class="form-horizontal" style="margin:100px auto;text-align:center;">
<div class="form-group" id="progress-group" style="display:block;">
<div id="size"></div>
<div class="progress">
<div id="progress" class="progress-bar progress-bar-success progress-bar-animated progress-bar-striped" role="progressbar"
aria-valuemin="0" aria-valuemax="100">
<span id="percentage"></span>
</div>
</div>
</div>
<div class="form-group" style="margin-top: 15px;">
<div class="row">
<div class="input-group">
<input name="file" id="file" type="file" class="form-control" aria-describedby="file" aria-label="Upload">
<button class="btn btn-outline-secondary" type="button" id="upload">上传</button>
<button class="btn btn-outline-danger" type="button" id="pause">暂停</button>
<button class="btn btn btn-outline-info" type="button" id="continue">继续</button>
</div>
</div>
</div>
</div>
- 接下来就是我们的js代码,引入tus脚本包和相关的js文件就可以了。
<script>
$(function () {
layui.use(['layer', 'util'], function () {
var layer = layui.layer,
util = layui.util;
var upload;
//上传
$('#upload').click(function () {
$('#progress-group').show();
var fileInput = $('#file').get(0).files[0];
if (!fileInput) {
layer.msg("请选择上传文件!");
return;
}
if (upload) {
layer.msg("已经选择过上传文件啦!");
return;
}
// 创建tus上传对象
upload = new tus.Upload($('#file')[0].files[0], {
// 文件服务器上传终结点地址设置
endpoint: "uploadfile/",
// 重试延迟设置
retryDelays: [0, 3000, 5000, 10000, 20000],
// 附件服务器所需的元数据
metadata: {
name: file.name,
contentType: file.type || 'application/octet-stream',
emptyMetaKey: ''
},
// 回调无法通过重试解决的错误
onError: function (error) {
console.log("Failed because: " + error)
},
// 上传进度回调
onProgress: onProgress,
// 上传完成后回调
onSuccess: function () {
layer.msg("上传成功,文件名:" + upload.file.name);
console.log("Download %s from %s", upload.file.name, upload.url)
upload=undefined;
}
})
upload.findPreviousUploads().then((previousUploads) => {
var chosenUpload = askToResumeUpload(previousUploads);
if (chosenUpload) {
upload.resumeFromPreviousUpload(chosenUpload);
}
upload.start();
});
// upload.start()
});
//暂停
$('#pause').click(function () {
if (!upload) {
layer.msg("请先开始上传!")
return;
}
if (upload._aborted) {
layer.msg("已经暂停上传啦!")
return;
}
upload.abort()
console.log(upload._aborted)
});
//继续
$('#continue').click(function () {
if (!upload) {
layer.msg("请先开始上传!")
return;
}
if (!upload._aborted) {
layer.msg("请先暂停上传!")
return;
}
upload.start()
console.log(upload._aborted)
});
function askToResumeUpload(previousUploads) {
if (previousUploads.length === 0) return null;
console.log(previousUploads);
var text = "系统查询到您之前上传过此文件是否恢复上传?:\n\n";
previousUploads.forEach((previousUpload, index) => {
text += "[" + index + "] " + util.toDateString(previousUpload.creationTime) + "\n";
});
text += "\n输入相应的号码恢复上传或按“取消”键重新上传";
var answer = prompt(text);
var index = parseInt(answer, 10);
if (!isNaN(index) && previousUploads[index]) {
return previousUploads[index];
}
}
//上传进度展示
function onProgress(bytesUploaded, bytesTotal) {
var percentage = (bytesUploaded / bytesTotal * 100).toFixed(2);
$('#progress').attr('aria-valuenow', percentage);
$('#progress').css('width', percentage + '%');
$('#percentage').html(percentage + '%');
var uploadBytes = byteToSize(bytesUploaded);
var totalBytes = byteToSize(bytesTotal);
$('#size').html(uploadBytes + '/' + totalBytes);
}
//将字节转换为Byte、KB、MB等
function byteToSize(bytes, separator = '', postFix = '') {
if (bytes) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.min(parseInt(Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 10), sizes.length - 1);
return `${(bytes / (1024 ** i)).toFixed(i ? 1 : 0)}${separator}${sizes[i]}${postFix}`;
}
return 'n/a';
}
});
});
</script>
- 我们创建一个新项目,引用一个tus包 tusdotnet
- 接下来就是添加中间件,并且配置我们的tus,这里我只实现了相关文件上传后的处理,以及上传文件后垃圾文件回收处理。
private static DefaultTusConfiguration CreateTusConfiguration(WebApplicationBuilder builder)
{
var env = builder.Environment.WebRootPath;
//文件上传路径
var tusFiles = Path.Combine(env, "tusfiles");
if (!Directory.Exists(tusFiles))
{
Directory.CreateDirectory(tusFiles);
}
return new DefaultTusConfiguration() {
UrlPath= "/uploadfile",
//文件存储路径
Store = new TusDiskStore(tusFiles),
//元数据是否允许空值
MetadataParsingStrategy = MetadataParsingStrategy.AllowEmptyValues,
//文件过期后不再更新
Expiration = new AbsoluteExpiration(TimeSpan.FromMinutes(5)),
//事件处理(各种事件,满足你所需)
Events = new Events
{
//上传完成事件回调
OnFileCompleteAsync = async ctx =>
{
//获取上传文件
var file = await ctx.GetFileAsync();
//获取上传文件元数据
var metadatas = await file.GetMetadataAsync(ctx.CancellationToken);
//获取上述文件元数据中的目标文件名称
var fileNameMetadata = metadatas["name"];
//目标文件名以base64编码,所以这里需要解码
var fileName = fileNameMetadata.GetString(Encoding.UTF8);
var extensionName = Path.GetExtension(fileName);
//将上传文件转换为实际目标文件
File.Move(Path.Combine(tusFiles, ctx.FileId), Path.Combine(tusFiles, $"{ctx.FileId}{extensionName}"));
var terminationStore = ctx.Store as ITusTerminationStore;
await terminationStore!.DeleteFileAsync(file.Id, ctx.CancellationToken);
}
}
};
}
使用我们的tus服务
// 配置tus 服务
app.UseTus(context=> CreateTusConfiguration(builder));
配置上传大小
builder.WebHost.ConfigureKestrel((context, options) =>
{
options.Limits.MaxRequestBodySize = long.MaxValue;
});
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现