工程建筑网页应用,C#在ASP.NET Core中如何实现大文件的分段续传与加密(示例)?
【一个C#外包仔的2G文件上传生死劫:从WebUploader到.NET Core自救指南】
"老板,这个需求…可能需要加钱。“我盯着客户发来的PDF,手指在"支持2G文件批量上传"那行字上疯狂颤抖。作为同时会修打印机和写ASP.NET Core的"全栈艺术家”,我深知这次要面对的是三重暴击:Vue2的兼容性、SQL Server的存储压力,还有那个该死的WebUploader——它现在就像个被拔掉电池的玩具,在控制台里躺着闪红灯。
第一幕:WebUploader的"临终表演"
"看!它动了!"我兴奋地指着屏幕上突然跳到99%的进度条,下一秒Chrome标签页就白屏了。这个百度开源的组件就像个行为艺术家:
- 分片上传?能工作,但偶尔会把第13片传到火星(后来发现是Nginx的
client_max_body_size在作怪) - 跨浏览器兼容?在Edge里会表演"进度条卡顿魔术",在Firefox里直接显示"NaN%"
- 断点续传?客户重启路由器后,所有分片都会集体玩"消失的她"
最绝的是错误处理:
// 前端发来的神秘代码
uploader.on('error', function(type) {
if(type === 'F_DUPLICATE') {
alert('文件已存在,但后端可能没收到这个消息');
} else {
console.log('出错了,但我不知道怎么描述');
}
});
第二幕:.NET Core与Vue2的"跨服聊天"
"前端说需要获取上传速度!"我冲着电话大喊,嘴里还嚼着辣条。后端小哥(其实也是我)的声线透着绝望:“哥,WebUploader的文档比我的C#注释还抽象…”
于是我们开启了量子纠缠式开发:
// 后端API(喝到第五杯咖啡后的产物)
[HttpPost("upload-chunk")]
public async Task UploadChunk(IFormFile file, string fileHash, int chunkIndex)
{
try {
var chunkPath = Path.Combine("uploads", fileHash, $"{chunkIndex}.part");
await using var stream = new FileStream(chunkPath, FileMode.Create);
await file.CopyToAsync(stream); // 偶尔会抛出"神秘异常"
return Ok(new { success = true }); // 其实可能没成功
} catch {
return StatusCode(500, "服务器说它累了");
}
}
// 前端调用(Vue2的魔法)
uploadChunk(chunk) {
const formData = new FormData();
formData.append('file', chunk.file);
formData.append('fileHash', this.fileHash);
formData.append('chunkIndex', chunk.index);
axios.post('/api/upload-chunk', formData, {
onUploadProgress: () => {
// 这个回调会随机触发3次
}
});
}
第三幕:SQL Server的"内存爆炸"
当客户问"能不能显示所有上传任务的历史记录"时,我盯着那台只有8G内存的云服务器陷入了沉思:
-- 最初的设计(天真版)
CREATE TABLE UploadTasks (
Id UNIQUEIDENTIFIER PRIMARY KEY,
FileName NVARCHAR(255),
FileSize BIGINT, -- 2G文件就是2147483648
Status INT, -- 0=上传中 1=完成 2=失败 3=合并中...
CreatedAt DATETIME2,
-- 省略了5个关联表的设计...
);
直到测试时发现:
- 插入1000条记录后,查询"进行中的任务"需要2.8秒
- 没有给
FileHash列加索引(别问我怎么知道的) - 当Nginx超时断开连接时,.NET Core还在傻乎乎地插入分片记录
- 合并文件时,
FileStream直接吃掉了4G内存
第四幕:绝地求生方案
经过三天三夜的谷歌搜索(和两包卫龙辣条),我制定了新方案:
前端改造计划:
- 弃用WebUploader,改用
uppy.io(至少文档是2024年的) - 实现真正的断点续传:
// 用IndexedDB存储已上传分片 const dbPromise = idb.open('UploadDB', 1, upgradeDB => { upgradeDB.createObjectStore('chunks', { keyPath: 'id' }); }); - 添加心跳检测,防止浏览器假死
- 用WebSocket实时显示上传速度(虽然.NET Core的SignalR更香,但学不动啊)
后端自救指南:
- 用
MemoryMappedFile替代普通FileStream处理大文件:// 合并分片时不再吃内存 using (var mmf = MemoryMappedFile.CreateFromFile("final.dat", FileMode.Create)) { for (int i = 0; i < totalChunks; i++) { var chunkPath = Path.Combine("uploads", fileHash, $"{i}.part"); // 内存映射文件操作... } } - 添加速率限制中间件:
// 防止客户端疯狂上传 app.Use(async (context, next) => { var clientIp = context.Connection.RemoteIpAddress; var rateLimitKey = $"upload:{clientIp}"; // 简单的Redis计数器(实际用StackExchange.Redis) if (redis.Increment(rateLimitKey) > 100) { context.Response.StatusCode = 429; await context.Response.WriteAsync("慢点,兄弟!"); return; } await next(); }); - 异步处理文件合并:
// 用Hangfire后台任务 _backgroundJobClient.Schedule( () => MergeFile(fileHash), TimeSpan.FromMinutes(1) // 延迟1分钟合并,给前端时间传完所有分片 );
数据库优化:
- 改用SQL Server的
FILESTREAM存储大文件元数据 - 添加Redis缓存当前上传任务
- 对大文件记录采用"软删除"策略(标记为删除但不真删,怕客户反悔)
- 分表策略:
-- 按年份分表 CREATE TABLE UploadTasks_2024 ( -- 结构同主表 );
终幕:测试日的疯狂
当客户终于发来测试文件时,我的监控面板是这样的:
- IIS错误日志:每分钟新增5条"Connection_Abandoned_By_ReqQueue"
- .NET Core内存占用突破1.8G
- SQL Server:慢查询日志里全是
SELECT * FROM UploadTasks WHERE Status=0
但!当那个2.1G的《Windows 11 ISO》终于显示"上传成功"时,我激动得把可乐打翻在键盘上——至少这次没把服务器宕机,只是让整个办公室的鼠标都变得黏糊糊的…
(客户反馈:IE11下进度条会跳《极乐净土》。我:微笑.jpg 并默默在Nginx配置里加了if ($http_user_agent ~* "MSIE") { return 403; })
设置框架
安装.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
创建数据库

配置数据库连接信息

检查数据库配置

访问页面进行测试

相关参考:
文件保存位置,
效果预览
文件上传

文件刷新续传
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
文件夹上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
批量下载
支持文件批量下载
下载续传
文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。
文件夹下载
支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。
下载完整示例
更多推荐
所有评论(0)