You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WebClient上传大文件报“The Stream was too long”问题解决

大文件上传问题:WebClient抛"The Stream was too long",HttpClient上传无法完成

问题背景

上传小于3GB的文件时,使用WebClient抛出The Stream was too long错误;改用HttpClient后,上传始终无法完成。

客户端代码

private async Task UpdateFile(string filePath, string route) 
{
    //System.Net.ServicePointManager.DefaultConnectionLimit = 200;
    System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
    Uri uri = new Uri(_httpClient.BaseAddress, relativeUri: route);//String.Format("{0}/api/TransferFiles/upload?loja={1}&name={2}", receiverUrl, loja, fi.Name));
    bool completed = false;
    using MyWebClient client = new MyWebClient();
    client.Headers.Add("Authorization", UserSession.Token);
    client.UploadFile(uri, filePath);
   return;
}
class MyWebClient : WebClient
{
    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        if (request is HttpWebRequest)
        {
            (request as HttpWebRequest).KeepAlive = false;
            ((HttpWebRequest)request).Timeout = System.Threading.Timeout.Infinite;
        }
        return request;
    }
} 

服务端代码

[RequestSizeLimit(10L * 1024L * 1024L * 1024L)]
[RequestFormLimits(MultipartBodyLengthLimit = 10L * 1024L * 1024L * 1024L,ValueCountLimit =int.MaxValue)]
[DisableFormValueModelBinding]
[HttpPost("Upload/World")]
public async Task<IActionResult> UploadFile(IFormFile file)
{

    string token = Request.Headers.FirstOrDefault(x => x.Key == "Authorization").Value;

    var details = await _authClient.UserDetailsAsync(token);
    if (!HasPermission(details))
        return Unauthorized();
    
    if (file == null || file.Length == 0)
    {
        // Handle the case where no file is uploaded
        return UnprocessableEntity(new MensagemDto("No file selected."));
    }
    if (file.Length > 4L * 1024 * 1024 * 1024) // 4GB Validation
    {
        return UnprocessableEntity(new MensagemDto("File can't be 4GB or higher."));
    }

    try
    {
        // Specify a directory to save the uploaded file
        var uploadPath = Path.Combine(DirectoryHelper.GetBaseDirectory(), "uploads");

        if (!Directory.Exists(uploadPath))
        {
            Directory.CreateDirectory(uploadPath);
        }

        // Generate a unique file name to avoid overwriting existing files
        var uniqueFileName = Guid.NewGuid().ToString() + "_" + file.FileName+".zip";

        // Combine the upload path with the unique file name
        var filePath = Path.Combine(uploadPath, uniqueFileName);

        // Copy the file to the specified path
        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }
        var service = new SetupService();
        service.SetupWorld(details, filePath);

        return Ok("File uploaded successfully.");
    }
    catch (ArgumentException ex)
    {
        // Handle any exceptions that occur during file upload
        return UnprocessableEntity(ex.Message);
    }
    catch (Exception ex)
    {
        // Handle any exceptions that occur during file upload
        return StatusCode(500, $"Internal server error: {ex.Message}");
    }
}  

问题原因分析

WebClient报错原因

WebClient底层依赖的HttpWebRequest默认最大请求长度为2GB——因为HttpWebRequest.ContentLength是int类型,最大值为2^31-1字节(约2.1GB),3GB文件超出该限制直接触发"The Stream was too long"错误。

HttpClient上传失败可能原因

  1. 默认超时时间过短,大文件传输未完成就被中断
  2. 未采用流式上传,一次性将文件加载到内存导致内存溢出或传输超时
  3. HttpClient实例配置不合理(如连接池、KeepAlive设置)

解决方案

方案1:WebClient临时修复(仅支持≤2GB文件)

若坚持使用WebClient,可关闭请求流缓存,但无法突破2GB上限:

class MyWebClient : WebClient
{
    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address) as HttpWebRequest;
        if (request != null)
        {
            request.KeepAlive = false;
            request.Timeout = System.Threading.Timeout.Infinite;
            // 关闭请求流缓存,避免内存加载整个文件
            request.AllowWriteStreamBuffering = false;
        }
        return request;
    }
}

注意:该方案无法支持3GB文件,仅适用于≤2GB场景。

方案2:HttpClient流式上传(推荐3GB场景)

使用HttpClient实现流式上传,避免内存过载,同时配置合理超时:

客户端表单上传代码(匹配服务端IFormFile)

private async Task UpdateFile(string filePath, string route)
{
    System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
    var uri = new Uri(_httpClient.BaseAddress, route);

    // 设置足够长的超时时间(根据文件大小调整,示例为30分钟)
    _httpClient.Timeout = TimeSpan.FromMinutes(30);

    // 以流式方式读取文件,避免加载到内存
    using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 8192, useAsync: true);
    using var formContent = new MultipartFormDataContent();
    var fileContent = new StreamContent(fileStream);
    fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
    // 参数名"file"需与服务端IFormFile参数名一致
    formContent.Add(fileContent, "file", Path.GetFileName(filePath));

    // 添加Authorization头
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", UserSession.Token);

    var response = await _httpClient.PostAsync(uri, formContent);
    response.EnsureSuccessStatusCode(); // 自动抛出HTTP错误状态码异常
}

服务端补充配置

在Program.cs中配置Kestrel的请求体大小限制,避免Kestrel提前截断大请求:

builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 10L * 1024 * 1024 * 1024; // 设置为10GB
});

额外优化建议

  • 客户端启用分块传输:显式设置fileContent.Headers.ContentLength = new FileInfo(filePath).Length;,HttpClient会自动处理分块传输
  • 服务端添加进度反馈:若需要上传进度,可在客户端监听StreamContent的传输进度,或服务端返回中间状态
  • 保持流式写入:服务端继续使用await file.CopyToAsync(stream),避免一次性读取文件到内存

内容的提问来源于stack exchange,提问作者Matheus Hoske

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:15:44