c#如何实现大文件分片上传

来源:站长工具作者:叶知晏头衔:草根站长
导读:本期聚焦于小伙伴创作的《c#如何实现大文件分片上传》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《c#如何实现大文件分片上传》有用,将其分享出去将是对创作者最好的鼓励。

在c#开发中处理大文件上传时,直接传输完整文件往往会遇到请求超时、服务器内存溢出、网络波动导致上传失败需要重传等问题,分片上传将大文件切割为多个小分片依次传输,能有效避免上述问题,同时支持断点续传提升上传体验。

c#如何实现大文件分片上传

大文件分片上传核心流程

完整的c#大文件分片上传流程分为四个核心步骤:

  • 前端将大文件按照固定大小切割为多个分片,为每个分片标记唯一标识、分片索引、总分片数等信息
  • 前端依次调用后端接口上传每个分片,后端接收后将分片临时存储到服务器指定目录
  • 前端在所有分片上传完成后,调用后端合并接口通知服务器合并所有分片
  • 后端校验所有分片完整性后,将临时分片合并为完整文件,清理临时分片数据

前端分片切割实现

前端使用JavaScript的File API实现文件分片切割,将切割后的分片通过FormData传递给后端,以下是示例代码:

// 定义分片大小,这里设置为2MB
const CHUNK_SIZE = 2 * 1024 * 1024;
// 要上传的大文件对象
const file = document.getElementById('fileInput').files[0];
// 生成文件唯一标识,避免同名文件冲突
const fileId = Date.now() + '_' + file.name;
// 计算总分片数
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);

// 循环切割分片并上传
for (let i = 0; i < totalChunks; i++) {
    // 计算当前分片的起始和结束位置
    const start = i * CHUNK_SIZE;
    const end = Math.min(start + CHUNK_SIZE, file.size);
    // 切割分片
    const chunk = file.slice(start, end);
    // 创建FormData携带分片信息
    const formData = new FormData();
    formData.append('fileId', fileId);
    formData.append('chunkIndex', i);
    formData.append('totalChunks', totalChunks);
    formData.append('fileName', file.name);
    formData.append('chunk', chunk);
    // 调用上传接口,这里使用fetch发送请求
    fetch('/api/upload/chunk', {
        method: 'POST',
        body: formData
    }).then(res => res.json())
    .then(data => {
        if (data.success) {
            console.log(`第${i+1}个分片上传成功`);
            // 所有分片上传完成后调用合并接口
            if (i === totalChunks - 1) {
                mergeChunks(fileId, file.name, totalChunks);
            }
        }
    });
}

// 合并分片请求方法
function mergeChunks(fileId, fileName, totalChunks) {
    fetch('/api/upload/merge', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            fileId: fileId,
            fileName: fileName,
            totalChunks: totalChunks
        })
    }).then(res => res.json())
    .then(data => {
        if (data.success) {
            console.log('文件合并成功,完整文件路径:' + data.filePath);
        }
    });
}

后端分片接收接口实现

后端使用ASP.NET Core Web API实现分片接收逻辑,接收前端传递的分片数据后,将分片临时存储到服务器的临时目录中,分片命名规则为文件标识_分片索引.tmp,方便后续合并时识别顺序。

using Microsoft.AspNetCore.Mvc;
using System.IO;

[ApiController]
[Route("api/upload")]
public class UploadController : ControllerBase
{
    // 临时分片存储目录,实际开发中可根据需求调整路径
    private readonly string _tempChunkDir = Path.Combine(Directory.GetCurrentDirectory(), "temp_chunks");
    // 完整文件存储目录
    private readonly string _finalFileDir = Path.Combine(Directory.GetCurrentDirectory(), "upload_files");

    public UploadController()
    {
        // 初始化目录,如果不存在则创建
        if (!Directory.Exists(_tempChunkDir))
        {
            Directory.CreateDirectory(_tempChunkDir);
        }
        if (!Directory.Exists(_finalFileDir))
        {
            Directory.CreateDirectory(_finalFileDir);
        }
    }

    /// <summary>
    /// 接收单个分片上传的接口
    /// </summary>
    [HttpPost("chunk")]
    public IActionResult UploadChunk([FromForm] string fileId, [FromForm] int chunkIndex, [FromForm] int totalChunks, [FromForm] string fileName, IFormFile chunk)
    {
        if (chunk == null || chunk.Length == 0)
        {
            return BadRequest(new { success = false, message = "分片数据为空" });
        }
        try
        {
            // 临时分片文件路径,格式为 文件标识_分片索引.tmp
            string chunkFilePath = Path.Combine(_tempChunkDir, $"{fileId}_{chunkIndex}.tmp");
            // 保存分片到临时目录
            using (var fileStream = new FileStream(chunkFilePath, FileMode.Create))
            {
                chunk.CopyTo(fileStream);
            }
            return Ok(new { success = true, message = "分片上传成功" });
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { success = false, message = $"分片上传失败:{ex.Message}" });
        }
    }
}

后端分片合并接口实现

所有分片上传完成后,前端调用合并接口,后端校验所有分片是否存在,然后按照分片索引顺序将分片合并为完整文件,合并完成后清理临时分片数据。

    /// <summary>
    /// 合并所有分片的接口
    /// </summary>
    [HttpPost("merge")]
    public IActionResult MergeChunks([FromBody] MergeRequest request)
    {
        try
        {
            // 校验所有分片是否都存在
            for (int i = 0; i < request.TotalChunks; i++)
            {
                string chunkFilePath = Path.Combine(_tempChunkDir, $"{request.FileId}_{i}.tmp");
                if (!System.IO.File.Exists(chunkFilePath))
                {
                    return BadRequest(new { success = false, message = $"第{i+1}个分片不存在,无法合并" });
                }
            }

            // 完整文件保存路径,避免同名文件覆盖,添加时间戳
            string finalFileName = $"{DateTime.Now:yyyyMMddHHmmss}_{request.FileName}";
            string finalFilePath = Path.Combine(_finalFileDir, finalFileName);

            // 按照分片索引顺序合并文件
            using (var finalStream = new FileStream(finalFilePath, FileMode.Create))
            {
                for (int i = 0; i < request.TotalChunks; i++)
                {
                    string chunkFilePath = Path.Combine(_tempChunkDir, $"{request.FileId}_{i}.tmp");
                    using (var chunkStream = new FileStream(chunkFilePath, FileMode.Open))
                    {
                        chunkStream.CopyTo(finalStream);
                    }
                    // 合并完成后删除临时分片
                    System.IO.File.Delete(chunkFilePath);
                }
            }

            return Ok(new { success = true, filePath = $"/upload_files/{finalFileName}" });
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { success = false, message = $"文件合并失败:{ex.Message}" });
        }
    }

    /// <summary>
    /// 合并请求的参数模型
    /// </summary>
    public class MergeRequest
    {
        public string FileId { get; set; }
        public string FileName { get; set; }
        public int TotalChunks { get; set; }
    }
}

断点续传实现思路

要实现断点续传,只需要在上传分片前,先调用后端接口查询已经上传成功的分片索引列表,前端跳过已上传的分片即可。后端新增查询已上传分片的接口:

    /// <summary>
    /// 查询已上传的分片索引列表,用于断点续传
    /// </summary>
    [HttpGet("uploaded_chunks")]
    public IActionResult GetUploadedChunks([FromQuery] string fileId, [FromQuery] int totalChunks)
    {
        List<int> uploadedChunks = new List<int>();
        for (int i = 0; i < totalChunks; i++)
        {
            string chunkFilePath = Path.Combine(_tempChunkDir, $"{fileId}_{i}.tmp");
            if (System.IO.File.Exists(chunkFilePath))
            {
                uploadedChunks.Add(i);
            }
        }
        return Ok(new { success = true, uploadedChunks = uploadedChunks });
    }

前端在上传前先调用这个接口获取已上传的分片列表,循环中跳过这些索引对应的分片即可实现断点续传。

注意事项

  • 分片大小建议设置为1MB到5MB之间,太小会增加请求次数,太大会失去分片上传的意义,可根据实际网络情况调整
  • 临时分片目录需要定期清理,避免服务器磁盘被无用临时文件占满,可以设置定时任务删除超过24小时的临时分片
  • 合并文件时需要校验分片的完整性,可以通过给每个分片添加MD5校验值,合并前校验分片MD5是否匹配,避免分片损坏导致文件不可用
  • 如果上传的是超大文件,建议对合并后的文件也做MD5校验,确保最终文件与原始文件一致
  • 接口需要做好权限校验,避免未授权用户上传文件占用服务器资源

C#大文件分片上传FileStreamMultipartFormDataContent分片合并修改时间:2026-06-30 06:39:50

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