
基于.NET实现大文件的断点续传功能,包含上传与下载。其中上传部分包括分片上传与断点续传功能,并借助WinForm实现上传进度反馈、并行上传、分片大小动态调整等辅助功能。
前言
断点续传基于分片上传和分片下载,包含断点续传上传与断点续传下载两部分,下面基于.NET分别对其进行功能实现。
断点续传上传
在文件上传场景中,分片上传和断点续传是处理大文件上传的重要技术。
除了基本的断点续传功能,大文件上传还应提供以下辅助功能:
- 分片大小调整:根据网络状况动态调整分片大小
- 并行上传:可以并行上传多个分片以提高速度
- 完整性校验:合并文件后校验哈希值确保文件完整
- 清理机制:定期清理未完成上传的临时分片
- 进度反馈:提供上传进度信息给用户
下面我们来简单实现上述功能。
分片上传
分片上传流程如下:
- 客户端首次上传前生成文件唯一ID(通常使用文件内容哈希,此处为了演示重新上传是从0开始的,使用的是Guid)
- 客户端将文件分割为固定大小的块,每块单独上传到服务器
- 服务器接收并临时保存每个分片
- 当所有分片上传完成后,服务器合并分片为完整文件
服务端实现
UploadChunk接口实现了分片的临时存储(存储的临时文件名为fileId_chunkNumber),以及分片的最终合并。
1 | [ ] |
客户端实现
首先创建一个ChunkedUploadService类,用来处理本地的文件流,并调用服务端的分片上传接口。
1 | public class ChunkedUploadService |
然后在客户端的UI线程中使用异步方式调用ChunkedUploadService类中的分片上传方法。
1 | var serviceUri = "http://localhost:5001"; |
断点续传
断点续传流程如下:
- 客户端首次上传前生成文件唯一ID
- 上传前根据文件文件唯一ID查询服务器已接收的分片,继续上传时只上传缺失的分片
- 当所有分片上传完成后合并分片
服务端实现
GetUploadStatus接口根据文件标识fileId查询已上传的分片编号,UploadChunk接口的功能同上。
1 | [ ] |
客户端实现
首先创建一个ResumableUploadService类,调用服务端的文件分片上传状态接口,对于已上传的分片跳过处理,然后调用服务端的分片上传接口继续上传,以实现断点续传功能。
1 | public class ResumableUploadService |
然后在客户端的UI线程中使用异步方式调用ResumableUploadService类中的断点续传方法。
1 | var serviceUri = "http://localhost:5001"; |
上传进度反馈与上传取消
通常情况下,大文件上传时需要给用户提供上传进度信息以及取消上传的功能,下面使用WinForm简单实现上述功能。
服务端实现
服务端代码延用断点续传,不再展示。
客户端实现
客户端的ResumableUploadService中的主要变化是添加了三个委托事件,分别用来更新上传百分比、状态消息、上传与取消按钮禁用状态。
1 | public class ResumableUploadService |
主窗体MainForm中分别绑定三个委托事件到相应的UI线程上,使用Invoke跨线程更新UI,实现上传过程中实时更新上传进度及状态信息。
Control.InvokeRequired属性指示调用方在对控件进行方法调用时是否必须调用Invoke方法,因为调用方位于创建控件所在的线程以外的线程中。Windows窗体中的控件绑定到特定线程,并且不是线程安全的。因此,如果要从其他线程调用控件的方法,则必须使用控件的调用方法之一来封送对正确线程的调用。
1 | public partial class MainForm : Form |
并行上传
并行上传是提升大文件传输效率的有效手段,并行上传可将总时间缩短为单线程上传时间/N(N为并行度)。并行上传适合在不稳定的网络环境(如高延迟)中使用,可充分利用间歇性网络带宽。
并行上传时多个分片同时上报上传进度,进度条呈现更平滑,更适合需要实时进度反馈的场景。
服务端实现
服务端代码延用断点续传,不再展示。
客户端实现
客户端基于ResumableUploadService,创建了一个并行上传服务类ParallelUploadService,使用Parallel.ForEachAsync
实现并行上传,并设置了最大并行度(默认为4)。
1 | public class ParallelUploadService |
主窗体MainForm中对于ParallelUploadService服务类的调用方式与ResumableUploadService类似,不再展示。
动态调整分片大小
根据网络状态动态调整分片大小,可以优化分片上传效率。
服务端实现
由于分片的大小是动态计算得出的,存储的临时文件名由fileId_chunkNumber变更为{fileId}/chunk_{offsetValue}。
服务端的分片存储结构如下:
uploads/
├── {fileId}/ // 每个文件一个独立目录
│ ├── chunk_{offsetValue1} // 分片文件按偏移量命名
│ ├── chunk_{offsetValue2}
│ └── …
└── completed/ // 最终合并的文件
由于上传是并行的,服务端在检查分片上传完整性时,必须以所有分片大小总和等于文件大小为标志,而不能以其中某一分片的偏移量+分片大小等于文件大小为标志。
1 | [ ] |
调用服务端上传时的日志记录如下,可以看到分片的上传是乱序的:

客户端实现
客户端实现了AdaptiveResumableUploadService服务类,在并行上传与断点续传的基础上,添加了实时更新网络指标(上行速度/网络延迟)并动态计算当前分片大小的功能,并使用了异步锁SemaphoreSlim确保分片信息更新时的原子性。
注:请勿使用同步锁lock,否则会报错Object synchronization method was called from an unsynchronized block of code
1 | public class AdaptiveResumableUploadService |
最终实现的效果如下:

完整性校验
完整性校验包含两个方面:
- 分片完整性校验:校验单个分片的完整性,用于检测网络传输错误(一般使用MD5校验)
- 文件完整性校验:校验合并后最终文件的完整性,确保内容安全(建议使用SHA-256校验)
在计算哈希值时应当仅读取文件内容,避免隐藏的元数据差异(文件创建/修改日期等)造成哈希值不同。
分片完整校验
以下为服务端UploadChunkAsync接口对于分片完整校验的部分代码:
1 | [ ] |
以下为客户端UploadChunkAsync方法对于分片完整性校验的部分代码:
1 | private async Task<bool> UploadChunkAsync(string fileId, int chunkIndex, long chunkOffset, int chunkSize, long fileSize, string fileName, Stream fileStream, CancellationToken cancellationToken) |
文件完整性校验
以下为服务端MergeChunksAsync接口中对于文件完整性校验的部分代码:
1 | [ ] |
重传尝试
分片上传过程中存在各种不稳定的因素(如网络波动),可以为客户端上传服务类添加重传尝试机制,在上传失败后自动重新上传。
1 | public class AdaptiveResumableUploadService |
分片定时清理
断点续传留下的分片会随着时间在服务器上越积越多,因此需要有一个分片定时清理策略。
首先在服务端创建一个后台任务配置类,用于读取appsettings.json中的后台任务配置项,并在Program.cs中注册配置项,
1 | public class BackgroundJobOptions |
1 | // 注册配置类 |
appsettings.json中的配置项如下所示。
1 | { |
然后定义IWorkServie和WorkService,作为分片定时清理服务类。
1 | public interface IWorkService |
1 | public class WorkService : IWorkService |
然后创建一个后台服务类UploadCleanupService继承BackgroundService,并调用WorkService中的分片定时清理服务。
1 | public class UploadCleanupService : BackgroundService |
最后在Program.cs中注册后台服务。
1 | // 注册后台服务 |
断点续传下载
与断点续传上传类似,断点续传下载同样包含下载进度反馈、下载取消、并行下载等辅助功能。
下面我们来简单实现上述功能。
下载进度反馈与下载取消
服务端实现
由于断点续传上传中使用了wwwroot静态资源文件夹,服务端的文件可直接通过url访问。确保Program.cs中启用了静态文件服务。
1 | // 启用静态文件服务 |
客户端实现
首先创建一个ResumableDownloadService服务类,根据传入的服务器文件地址,使用Range头请求从断点处继续下载(确保服务器支持Range请求头),并提供进度条反馈和取消下载功能。
1 | public class ResumableDownloadService |
与上传类似,主窗体MainForm中的代码如下。
1 | public partial class MainForm : Form |
并行下载与暂停恢复
客户端实现
首先创建一个ParallelDownloadService服务类,默认并发下载线程数为4,并使用并发字典记录CancellationTokenSource,以便实现下载的暂停和恢复。
1 | public class ParallelDownloadService |
主窗体MainForm中的代码如下。
1 | public partial class MainForm : Form |
下载重试
与上传类似,断点续传下载也可以添加重试机制。
1 | public class ParallelDownloadService |
限速下载
断点续传下载同样可以添加下载速度限制功能。下面创建一个SpeedLimitResumableDownloadService服务类,在并行下载基础上,添加限速下载功能。
1 | public class SpeedLimitResumableDownloadService |
主窗体MainForm中的部分代码如下。
1 | public partial class MainForm : Form |
最终实现的效果如下:
