- A+
所属分类:.NET技术
.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 = "系统查询到您之前上传过此文件是否恢复上传?:nn"; 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; });