C#计算下载进度百分比时在33%左右突然变为负数如何解决?
问题根源
你遇到的进度负数是32位有符号整数溢出导致的:
- C#中
int是32位有符号整数,最大值为2^31 - 1 = 2147483647 - 计算
100 * totalBytesCopied时,两个操作数都是int类型,会先按int规则计算再转double:当totalBytesCopied超过21474836时,100 * totalBytesCopied就会超过int最大值,溢出后值变为负数,最终计算出的进度自然为负。 - 同时你的代码还存在一个潜在的文件损坏bug:
WriteAsync传入的是buffer.Length而非实际读取到的bytesRead,如果最后一次读取未填满缓冲区,会将缓冲区多余的无效数据写入目标文件,导致下载的视频损坏。
修复方案
- 最优方案:将所有计数相关的变量改为
long类型,从根本上避免溢出(支持最大2^63-1字节的文件,完全满足视频下载需求):
private async Task CopyStream( Lecture lecture, Stream source, Stream destination, long sourceLength, // int改long CancellationToken token, int bufferSize = (16 * 1024)) { var buffer = new byte[bufferSize]; if (sourceLength <= 0) return; long totalBytesCopied = 0; // int改long int bytesRead = -1; while (bytesRead != 0) { bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, token); if (bytesRead == 0) break; await destination.WriteAsync(buffer, 0, bytesRead, token); // buffer.Length改bytesRead totalBytesCopied += bytesRead; var progress = (int)Math.Round((double)(100 * totalBytesCopied) / sourceLength); Debug.WriteLine( $"Lecture={lecture.Title}, " + $"sourceLength={sourceLength}, " + $"totalBytesCopied={totalBytesCopied}, " + $"bytesRead={bytesRead}, progress={progress}"); RaiseLectureDownloadProgressChangedEvent(null, new LectureDownloadProgressChangedEventArgs(lecture, progress)); } }
- 临时兼容方案:如果不想修改入参类型,可以调整计算顺序,先转浮点型再做乘法,避免整数溢出:
var progress = (int)Math.Round(100 * (double)totalBytesCopied / sourceLength);
内容的提问来源于stack exchange,提问作者user9105459
相关产品推荐
相关产品推荐

