news 2026/5/27 12:37:59

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/5/28 8:46:08

【Open-AutoGLM智能回复揭秘】:如何用AI实现消息自动响应效率提升90%?

第一章&#xff1a;Open-AutoGLM智能回复技术概述Open-AutoGLM 是一种基于生成式语言模型的智能回复系统&#xff0c;旨在实现高效、准确且上下文感知的自动化文本响应。该技术融合了大规模预训练语言模型与动态上下文理解机制&#xff0c;能够广泛应用于客服系统、智能助手和社…

作者头像 李华
网站建设 2026/5/28 4:17:18

基于Excalidraw的定制化企业白板解决方案

基于 Excalidraw 的定制化企业白板解决方案 在远程办公常态化、跨地域协作日益频繁的今天&#xff0c;团队对“看得见”的沟通方式提出了更高要求。无论是产品原型讨论、系统架构设计&#xff0c;还是敏捷迭代中的任务拆解&#xff0c;一张能实时共创的虚拟白板&#xff0c;早…

作者头像 李华
网站建设 2026/5/27 13:32:27

Excalidraw图层管理功能进阶用法

Excalidraw图层管理功能进阶用法 在远程协作日益成为常态的今天&#xff0c;技术团队对可视化工具的需求早已超越“画个框连条线”的初级阶段。一张清晰、可维护、易于沟通的架构图&#xff0c;往往决定了需求评审能否高效推进&#xff0c;或是故障复盘是否能快速达成共识。Exc…

作者头像 李华
网站建设 2026/5/26 5:16:40

Excalidraw绘图快捷键大全(建议收藏)

Excalidraw绘图快捷键大全&#xff08;建议收藏&#xff09; 在远程协作和敏捷开发成为常态的今天&#xff0c;团队对可视化工具的要求早已不再局限于“能画图”。我们需要的是一个既能激发创意、又不拖慢节奏的数字白板——而 Excalidraw 正是这样一款让人一用就回不去的工具。…

作者头像 李华
网站建设 2026/5/26 4:32:27

Excalidraw性能调优:大规模图形渲染优化

Excalidraw性能调优&#xff1a;大规模图形渲染优化 在现代远程协作日益深入的背景下&#xff0c;可视化工具早已不再是“锦上添花”的辅助软件&#xff0c;而是产品设计、系统架构和团队沟通的核心载体。Excalidraw 凭借其独特的手绘风格、轻量级交互和出色的可扩展性&#xf…

作者头像 李华