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

C#写入4GB+大文件时出现中断的问题求助

问题分析与解决思路

核心问题定位

你遇到的写入到约4MB(对应你说的4194300字节左右)位置停止的问题,核心原因集中在几个新手常踩的流操作坑:

  1. 错误用文本流处理二进制数据:StreamWriter是为文本设计的,会对字节做编码转换,完全不适合MP4这类二进制文件,而且它的内部缓冲会和外层BufferedStream冲突,导致数据截断或写入停滞。
  2. 异步方法阻塞调用+异常吞吃:用Wait()阻塞异步写入,不仅容易引发死锁,而且空catch吞掉了所有异常——你根本不知道是IO错误、取消令牌触发还是controller.GetCount出了问题。
  3. 循环终止条件不严谨:只靠count==0终止循环,没判断是否已经达到endbyte的结束位置,可能导致逻辑卡壳。
  4. 资源未正确释放:流对象没加using包裹,可能导致缓冲未完全写入就被回收。

具体修复步骤

1. 移除StreamWriter,直接用BufferedStream处理二进制流

把包裹网络流的StreamWriter彻底删掉,二进制流不需要文本编码转换,直接用BufferedStream包装即可:

// 替换原_outputStream初始化代码
this._outputStream = new BufferedStream(this._tcpClient.GetStream());
// 二进制流不需要AutoFlush,手动Flush或靠using自动处理即可

2. 替换阻塞异步调用为异步方法,避免死锁

把整个流程改成异步,用await代替Wait(),同时绝对不要吞吃异常——至少打日志方便调试:

// Process改成异步方法
public async Task ProcessAsync()
{
    // 用using自动释放流资源,避免内存泄漏和缓冲丢失
    using (var outputStream = new BufferedStream(this._tcpClient.GetStream()))
    using (var fileStream = new FileStream(filename, FileMode.Create))
    {
        try
        {
            await HandleGetRequestAsync(outputStream, fileStream, this._startByte, this._endByte, this._fileLength);
        }
        catch (Exception ex)
        {
            // 不要吞异常,至少记录错误信息
            Console.WriteLine($"处理失败:{ex.Message}\n{ex.StackTrace}");
            this.WriteFailure();
        }
    }
}

// HandleGetRequest改成异步方法
public async Task HandleGetRequestAsync(Stream outputStream, FileStream fileStream, long startByte, long endByte, long fileLength)
{
    try
    {
        // 用标准1MB缓冲,避免奇怪的大小(1024*1024=1048576)
        byte[] buffer = new byte[1024 * 1024];
        while (true)
        {
            // 先判断是否已到结束位置,避免无效读取
            if (startByte >= endByte)
                break;
            
            // 计算实际能读的字节数,不超过剩余长度和缓冲大小
            long maxRead = Math.Min(endByte - startByte, buffer.Length);
            long count = this.controller.GetCount(startByte, buffer, (int)maxRead);
            
            // 读取到0字节终止循环
            if (count <= 0)
                break;
            
            startByte += count;
            // 用await异步写入,避免阻塞和死锁
            await outputStream.WriteAsync(buffer, 0, (int)count, this._cancellationToken);
            await fileStream.WriteAsync(buffer, 0, (int)count, this._cancellationToken);
        }
        // 手动Flush确保所有缓冲写入磁盘/网络
        await outputStream.FlushAsync(this._cancellationToken);
        await fileStream.FlushAsync(this._cancellationToken);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("操作被用户/系统取消");
    }
    catch (IOException ex)
    {
        Console.WriteLine($"IO错误:{ex.Message}");
        throw; // 重新抛出让上层处理
    }
}

3. 排查controller.GetCount的逻辑

你提到的停止位置刚好是4MB左右,大概率是GetCount方法在读取到该位置时返回了异常或0,但被原代码的空catch吞掉了。建议在调用GetCount前后加日志,输出每次的startByte和count值,看是不是这个方法本身有内部限制(比如单次读取最大4MB)。

4. 验证文件写入权限和磁盘空间

确保程序有目标文件夹的写入权限,且磁盘有足够空间——虽然小文件正常,但大文件可能遇到权限限制或磁盘空间不足的问题。


额外注意事项

  • 永远不要混用文本流和二进制流:处理视频、图片、压缩包这类二进制数据,只用Stream、BufferedStream、FileStream这类原始字节流,别碰StreamWriter/StreamReader。
  • 用using管理所有IDisposable资源:流、数据库连接等实现IDisposable的对象,必须用using包裹,确保自动释放资源。
  • 绝对不要写空catch:空catch是调试的噩梦,至少要记录异常信息,才能快速定位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 22:36:29