C#写入4GB+大文件时出现中断的问题求助
问题分析与解决思路
核心问题定位
你遇到的写入到约4MB(对应你说的4194300字节左右)位置停止的问题,核心原因集中在几个新手常踩的流操作坑:
- 错误用文本流处理二进制数据:
StreamWriter是为文本设计的,会对字节做编码转换,完全不适合MP4这类二进制文件,而且它的内部缓冲会和外层BufferedStream冲突,导致数据截断或写入停滞。 - 异步方法阻塞调用+异常吞吃:用
Wait()阻塞异步写入,不仅容易引发死锁,而且空catch吞掉了所有异常——你根本不知道是IO错误、取消令牌触发还是controller.GetCount出了问题。 - 循环终止条件不严谨:只靠
count==0终止循环,没判断是否已经达到endbyte的结束位置,可能导致逻辑卡壳。 - 资源未正确释放:流对象没加
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
相关产品推荐
相关产品推荐

