news 2026/4/17 21:30:02

C#在.NET MVC中如何设计大附件上传的进度监控界面?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#在.NET MVC中如何设计大附件上传的进度监控界面?

开发者日记:2023年X月X日 星期X 长沙 晴

项目背景
今日正式启动客户的大文件传输系统项目,需求明确:支持20G文件/文件夹上传下载、跨平台(Windows/macOS/Linux)、全浏览器兼容(含IE8)、断点续传、保留文件夹层级结构。后端使用**.NET Core**(而非PHP),数据库改为SQL Server,存储仍用阿里云OSS,前端为Vue3 CLI + WebUploader/H5。客户强调高频文件夹上传场景,需极致优化用户体验。免费开源代码和7*24支持的压力依旧,但技术栈调整带来新挑战。


技术选型与调整

  1. 前端框架

    • Vue3 CLI:维持组件化开发,但需适配IE8的Polyfill(如babel-polyfill)。
    • WebUploader:核心上传组件,需深度定制文件夹解析逻辑。
    • H5 File API:现代浏览器备用方案,IE8回退到Flash上传。
  2. 后端架构

    • .NET Core:替代PHP,利用IFormFile处理分片,System.Data.SqlClient操作SQL Server。
    • SQL Server:存储文件元数据(路径、分片状态、用户ID)。
  3. 核心难点升级

    • 高频文件夹上传:需优化递归解析性能,避免前端卡顿。
    • 断点续传稳定性:SQL Server事务保证分片记录一致性。
    • IE8兼容性:Flash上传需处理跨域问题(crossdomain.xml)。

前端代码优化(Vue3 + WebUploader)

1. 强化文件夹上传逻辑
// src/components/FolderUploader.vueimport{ref,onMounted}from'vue';importWebUploaderfrom'webuploader';import'webuploader/dist/webuploader.css';exportdefault{setup(){constuploader=ref(null);constfolderTree=ref([]);// 存储文件夹层级结构onMounted(()=>{// 动态加载Flash(IE8兼容)if(!WebUploader.Uploader.support('HTML5')){WebUploader.Uploader.register({name:'flash',fn:()=>'/assets/Uploader.swf'});}uploader.value=newWebUploader.Uploader({swf:'/assets/Uploader.swf',server:'/api/upload',pick:'#folderPicker',chunked:true,chunkSize:4*1024*1024,// 4MB分片formData:{fileId:'',isDir:false,relativePath:''// 关键:记录文件相对路径}});// 监听文件夹选择(需配合input[directory])document.getElementById('folderInput').addEventListener('change',(e)=>{constitems=e.target.files;if(items){parseFolder(items);// 递归解析文件夹}});});// 递归解析文件夹(兼容现代浏览器)constparseFolder=(items)=>{consttree=[];for(leti=0;i<items.length;i++){constfile=items[i];constpath=file.webkitRelativePath||file.name;// 相对路径constsegments=path.split('/');// 构建树形结构letcurrentLevel=tree;segments.slice(0,-1).forEach(segment=>{letdir=currentLevel.find(item=>item.name===segment);if(!dir){dir={name:segment,type:'directory',children:[]};currentLevel.push(dir);}currentLevel=dir.children;});// 添加文件节点currentLevel.push({name:segments.pop(),type:'file',file:file,relativePath:path});}folderTree.value=tree;uploadFolder(tree);};// 上传文件夹内容constuploadFolder=(tree)=>{tree.forEach(node=>{if(node.type==='directory'){uploadFolder(node.children);// 递归上传子目录}else{constformData=uploader.value.option('formData');formData.relativePath=node.relativePath;uploader.value.addFile(node.file,node.relativePath);}});};return{uploader,folderTree};}};
2. IE8兼容性处理

后端代码实现(.NET Core + SQL Server)

1. 分片上传接口
// Controllers/UploadController.csusingMicrosoft.AspNetCore.Mvc;usingSystem.Data.SqlClient;usingSystem.IO;[ApiController][Route("api/upload")]publicclassUploadController:ControllerBase{privatereadonlyIConfiguration_config;publicUploadController(IConfigurationconfig)=>_config=config;[HttpPost]publicasyncTaskUpload([FromForm]UploadModelmodel){varfileId=model.FileId??Guid.NewGuid().ToString();vartempDir=Path.Combine("/tmp/uploads",fileId);Directory.CreateDirectory(tempDir);// 保存分片varchunkPath=Path.Combine(tempDir,$"chunk_{model.ChunkIndex}");using(varstream=newFileStream(chunkPath,FileMode.Create)){awaitmodel.File.CopyToAsync(stream);}// 记录分片状态到SQL Serverusing(varconn=newSqlConnection(_config.GetConnectionString("Default"))){awaitconn.OpenAsync();varcmd=newSqlCommand(@"MERGE INTO UploadProgress AS target USING (VALUES (@fileId, @chunkIndex, @totalChunks)) AS source (FileId, ChunkIndex, TotalChunks) ON target.FileId = source.FileId AND target.ChunkIndex = source.ChunkIndex WHEN NOT MATCHED THEN INSERT (FileId, ChunkIndex, TotalChunks, UploadedAt) VALUES (source.FileId, source.ChunkIndex, source.TotalChunks, GETDATE());",conn);cmd.Parameters.AddWithValue("@fileId",fileId);cmd.Parameters.AddWithValue("@chunkIndex",model.ChunkIndex);cmd.Parameters.AddWithValue("@totalChunks",model.TotalChunks);awaitcmd.ExecuteNonQueryAsync();}// 如果是最后一块,合并并上传OSSif(model.ChunkIndex==model.TotalChunks-1){varfinalPath=Path.Combine(tempDir,"final_file");using(varoutput=System.IO.File.Create(finalPath)){for(inti=0;i<model.TotalChunks;i++){varchunk=System.IO.File.ReadAllBytes(Path.Combine(tempDir,$"chunk_{i}"));awaitoutput.WriteAsync(chunk,0,chunk.Length);}}// 上传OSS(需引入阿里云OSS SDK)varclient=newOSSClient("endpoint","accessKey","secretKey");awaitclient.PutObjectAsync("bucket-name",$"uploads/{fileId}",finalPath);// 清理临时文件Directory.Delete(tempDir,true);}returnOk(new{success=true,fileId});}}publicclassUploadModel{publicIFormFileFile{get;set;}publicstringFileId{get;set;}publicintChunkIndex{get;set;}publicintTotalChunks{get;set;}publicstringRelativePath{get;set;}// 文件夹层级路径}
2. SQL Server表结构
CREATETABLEUploadProgress(IdINTIDENTITY(1,1)PRIMARYKEY,FileId NVARCHAR(64)NOTNULL,ChunkIndexINTNOTNULL,TotalChunksINTNOTNULL,UploadedAt DATETIME2DEFAULTGETDATE(),UNIQUE(FileId,ChunkIndex));

断点续传实现

1. 前端恢复逻辑
// 检查未完成上传constcheckResume=async(fileId)=>{constres=awaitfetch(`/api/upload/progress?fileId=${fileId}`);constdata=awaitres.json();if(data.completedChunks<data.totalChunks){uploader.value.option('formData',{fileId,chunk:data.completedChunks});uploader.value.upload();}};// 本地存储fileId(即使浏览器关闭)window.addEventListener('beforeunload',()=>{if(uploader.value.getFiles().length>0){localStorage.setItem('lastUploadId',uploader.value.option('formData').fileId);}});
2. 后端进度查询
[HttpGet("progress")]publicasyncTaskGetProgress(stringfileId){using(varconn=newSqlConnection(_config.GetConnectionString("Default"))){awaitconn.OpenAsync();varcmd=newSqlCommand("SELECT COUNT(*) AS Completed FROM UploadProgress WHERE FileId = @fileId",conn);cmd.Parameters.AddWithValue("@fileId",fileId);varcount=(int)awaitcmd.ExecuteScalarAsync();returnOk(new{completedChunks=count});}}

今日总结

  • 进展:完成文件夹层级解析和.NET Core分片上传逻辑,IE8兼容性方案验证通过。
  • 问题
    1. WebUploader在IE8下对大文件夹性能较差,需优化DOM操作。
    2. SQL Server事务需加强,避免分片记录残留。
  • 明日计划
    1. 实现OSS分片上传(避免本地合并临时文件)。
    2. 编写完整的错误处理和日志系统。

求助:若有熟悉.NET Core文件处理或SQL Server优化的高手,欢迎加入QQ群374992201指导!代码将完全开源回馈社区。


(注:实际项目需补充安全校验、OSS直传和性能监控代码。)

设置框架

安装.NET Framework 4.7.2
https://dotnet.microsoft.com/en-us/download/dotnet-framework/net472
框架选择4.7.2

添加3rd引用

编译项目

NOSQL

NOSQL无需任何配置可直接访问页面进行测试

SQL

使用IIS
大文件上传测试推荐使用IIS以获取更高性能。

使用IIS Express

小文件上传测试可以使用IIS Express

创建数据库

配置数据库连接信息

检查数据库配置

访问页面进行测试


相关参考:
文件保存位置,

效果预览

文件上传

文件刷新续传

支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传

文件夹上传

支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。

批量下载

支持文件批量下载

下载续传

文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。

文件夹下载

支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。

下载完整示例

下载完整示例

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:34:47

Excalidraw开源项目部署教程(含GPU加速优化)

Excalidraw 开源项目部署与 GPU 加速优化实践 在远程协作日益成为主流工作模式的今天&#xff0c;团队对高效、直观的可视化工具需求愈发迫切。尤其在技术设计、产品原型和架构规划等场景中&#xff0c;一张草图往往胜过千言万语。然而&#xff0c;传统绘图软件如 Visio 或 Lu…

作者头像 李华
网站建设 2026/4/18 3:25:03

Excalidraw离线版部署方案(支持内网环境)

Excalidraw 离线部署方案&#xff1a;构建内网安全协作白板 在企业数字化转型不断深入的今天&#xff0c;技术团队对可视化协作工具的需求早已超越“能画图”的基础阶段。尤其是在金融、军工、科研和大型制造等行业中&#xff0c;网络隔离与数据安全成为刚性要求——任何依赖公…

作者头像 李华
网站建设 2026/4/18 3:26:42

Excalidraw自定义主题设置:打造品牌专属白板

Excalidraw 自定义主题设置&#xff1a;打造品牌专属白板 在远程协作日益成为常态的今天&#xff0c;团队对可视化工具的需求早已超越了“能画图”这一基本功能。从产品设计评审到系统架构推演&#xff0c;一张清晰、专业且风格统一的白板图&#xff0c;往往能在关键时刻提升沟…

作者头像 李华
网站建设 2026/4/18 3:36:19

常识推理:当前AI系统的瓶颈与突破口

常识推理:当前AI系统的瓶颈与突破口 关键词:常识推理、AI系统、瓶颈、突破口、知识表示、推理算法 摘要:本文围绕常识推理这一关键议题,深入剖析了当前AI系统在常识推理方面面临的诸多瓶颈,包括知识表示的局限性、推理算法的不足等。同时,详细探讨了可能的突破口,如新型…

作者头像 李华
网站建设 2026/4/17 17:05:00

Java小白求职者的面试经历与技术解析:从Spring Boot到微服务架构

文章简述 本文详细描述了一位Java小白在互联网大厂求职面试中的经历&#xff0c;通过面试官的层层提问&#xff0c;逐步揭示了Spring Boot、微服务架构等技术要点&#xff0c;并在每轮提问后提供详细的答案解析&#xff0c;帮助读者更好地理解相关技术。 正文 场景介绍 在一家知…

作者头像 李华
网站建设 2026/4/18 3:36:30

Excalidraw与PlantUML结合:代码化绘图新思路

Excalidraw与PlantUML结合&#xff1a;代码化绘图新思路 在远程协作日益频繁的今天&#xff0c;技术团队常常面临这样一个尴尬局面&#xff1a;架构师花了一小时精心画出一张服务调用关系图&#xff0c;结果第二天需求变更&#xff0c;整张图几乎要重做。更糟的是&#xff0c;当…

作者头像 李华