C#如何设计支持断点续传的文件上传API

来源:AI教程网作者:小何头衔:草根站长
导读:本期聚焦于小伙伴创作的《C#如何设计支持断点续传的文件上传API》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#如何设计支持断点续传的文件上传API》有用,将其分享出去将是对创作者最好的鼓励。

在C#服务端实现支持断点续传的文件上传API,核心思路是通过分片上传、记录上传进度、校验已上传分片的方式,让客户端在中断后可以从上次停止的位置继续上传,避免重复传输已完成的文件内容。

C#如何设计支持断点续传的文件上传API

断点续传的核心原理

断点续传的实现依赖三个关键步骤:

  • 客户端将大文件拆分为固定大小的分片,按顺序上传每个分片
  • 服务端接收分片后,将分片临时存储,并记录当前文件已上传的分片信息
  • 上传中断后,客户端先向服务端查询已上传的分片列表,跳过已上传的部分,继续上传剩余分片

所有分片上传完成后,服务端将临时分片合并为完整的原始文件,完成整个上传流程。

API接口设计

我们需要设计两个核心接口,分别用于查询上传进度和上传文件分片,接口参数定义如下:

接口功能请求方式请求路径核心参数
查询上传进度GET/api/upload/progressfileId:文件的唯一标识,由客户端生成
上传文件分片POST/api/upload/chunkfileId:文件唯一标识,chunkIndex:当前分片索引,totalChunks:总分片数,chunkData:分片二进制数据

服务端核心实现代码

存储目录配置

首先定义临时分片存储目录和最终文件存储目录,确保目录存在:

using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Collections.Generic;
using System.Linq;

namespace BreakpointUploadDemo.Controllers
{
    [ApiController]
    [Route("api/upload")]
    public class UploadController : ControllerBase
    {
        // 临时分片存储目录
        private readonly string _tempChunkDir = Path.Combine(Directory.GetCurrentDirectory(), "UploadTemp", "Chunks");
        // 最终文件存储目录
        private readonly string _finalFileDir = Path.Combine(Directory.GetCurrentDirectory(), "UploadTemp", "Final");

        public UploadController()
        {
            // 确保目录存在,不存在则创建
            if (!Directory.Exists(_tempChunkDir))
            {
                Directory.CreateDirectory(_tempChunkDir);
            }
            if (!Directory.Exists(_finalFileDir))
            {
                Directory.CreateDirectory(_finalFileDir);
            }
        }
    }
}

查询上传进度接口实现

该接口根据fileId查询已上传的分片索引,返回给客户端:

[HttpGet("progress")]
public IActionResult GetUploadProgress([FromQuery] string fileId)
{
    if (string.IsNullOrEmpty(fileId))
    {
        return BadRequest(new { code = 400, message = "fileId不能为空" });
    }
    // 获取该文件对应的所有分片文件
    var chunkFiles = Directory.GetFiles(_tempChunkDir, $"{fileId}_*.chunk");
    // 提取已上传的分片索引
    List<int> uploadedChunks = new List<int>();
    foreach (var chunkFile in chunkFiles)
    {
        var fileName = Path.GetFileName(chunkFile);
        // 文件名格式为 fileId_分片索引.chunk
        var indexStr = fileName.Replace($"{fileId}_", "").Replace(".chunk", "");
        if (int.TryParse(indexStr, out int chunkIndex))
        {
            uploadedChunks.Add(chunkIndex);
        }
    }
    // 排序后返回
    uploadedChunks.Sort();
    return Ok(new { code = 200, uploadedChunks });
}

上传分片接口实现

接收客户端上传的分片,保存到临时目录,当所有分片上传完成后自动合并文件:

[HttpPost("chunk")]
public IActionResult UploadChunk([FromForm] string fileId, [FromForm] int chunkIndex, [FromForm] int totalChunks, IFormFile chunkData)
{
    if (string.IsNullOrEmpty(fileId) || chunkData == null || chunkIndex < 0 || totalChunks <= 0)
    {
        return BadRequest(new { code = 400, message = "参数不完整" });
    }
    try
    {
        // 分片文件命名规则:fileId_分片索引.chunk
        string chunkFileName = $"{fileId}_{chunkIndex}.chunk";
        string chunkFilePath = Path.Combine(_tempChunkDir, chunkFileName);
        // 保存分片到临时目录
        using (var stream = new FileStream(chunkFilePath, FileMode.Create))
        {
            chunkData.CopyTo(stream);
        }
        // 检查是否所有分片都已上传完成
        var allChunkFiles = Directory.GetFiles(_tempChunkDir, $"{fileId}_*.chunk");
        if (allChunkFiles.Length == totalChunks)
        {
            // 所有分片上传完成,执行合并操作
            string finalFileName = $"{fileId}_{Path.GetFileName(chunkData.FileName)}";
            string finalFilePath = Path.Combine(_finalFileDir, finalFileName);
            // 按分片索引顺序合并文件
            using (var finalStream = new FileStream(finalFilePath, FileMode.Create))
            {
                for (int i = 0; i < totalChunks; i++)
                {
                    string currentChunkPath = Path.Combine(_tempChunkDir, $"{fileId}_{i}.chunk");
                    using (var chunkStream = new FileStream(currentChunkPath, FileMode.Open))
                    {
                        chunkStream.CopyTo(finalStream);
                    }
                    // 合并完成后删除临时分片
                    System.IO.File.Delete(currentChunkPath);
                }
            }
            return Ok(new { code = 200, message = "文件上传完成", filePath = finalFilePath });
        }
        return Ok(new { code = 200, message = $"分片{chunkIndex}上传成功" });
    }
    catch (System.Exception ex)
    {
        return StatusCode(500, new { code = 500, message = $"上传失败:{ex.Message}" });
    }
}

关键注意事项

  • fileId需要保证唯一性,客户端可以使用文件名+文件大小+修改时间的哈希值生成,避免不同文件冲突
  • 分片大小建议设置为1-5MB,过小会增加请求次数,过大会降低断点续传的效率
  • 可以定期清理超过一定时间的临时分片文件,避免占用过多磁盘空间
  • 如果需要对上传文件做大小限制,可以在接口中添加校验逻辑,超过限制直接返回错误
断点续传的核心是进度持久化,上述示例将进度存储在本地文件系统,生产环境中可以替换为Redis等存储,提升并发场景下的性能。

C#断点续传文件上传API设计修改时间:2026-07-14 23:24:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。