如何用QNetwork测量下载进度?解决下载进度异常问题
QNetwork下载进度异常(bytesTotal为-1)问题排查与修复
问题现象
使用QNetwork实现文件下载时,偶尔出现以下异常输出:
Percent complete: -1.08905e+09 Downloaded 11623330 of -1
随后下载失败,保存的zip文件大小为0字节。
原始代码
QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkRequest request; // Random link just to test: request.setUrl( QUrl("https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n5.0.2.zip")); QNetworkReply *reply = manager->get(request); connect(reply, &QNetworkReply::downloadProgress, [this, reply](qint64 bytesReceived, qint64 bytesTotal) { qDebug() << "Downloaded " << bytesReceived << " of " << bytesTotal; double percentComplete = (bytesReceived * 100.0) / bytesTotal; qDebug() << "Percent complete: " << percentComplete; }); connect(reply, &QNetworkReply::finished, [this, reply]() { if (reply->error() != QNetworkReply::NoError) { qDebug() << "Error: " << reply->errorString(); } else { QString fileName = "C:/Users/Raja/Downloads/file.zip"; QFile file(fileName); if (file.open(QIODevice::WriteOnly)) { file.write(reply->readAll()); file.close(); qDebug() << "File downloaded successfully"; } else qDebug() << "Error: Unable to open the file"; } reply->deleteLater(); });
问题原因
- bytesTotal为-1的本质:该值表示服务器未返回
Content-Length响应头,或请求过程中发生网络错误(如连接中断、重定向异常)。 - 非法数值计算:代码直接用bytesTotal做除法,当它为-1时会产生无效的百分比值。
- 错误处理不完整:下载出错时未清理已创建的空文件,且
readAll()返回空数据,导致文件大小为0。
修复方案
1. 校验bytesTotal有效性
计算进度前先判断bytesTotal是否大于0,避免非法运算:
connect(reply, &QNetworkReply::downloadProgress, [this](qint64 bytesReceived, qint64 bytesTotal) { qDebug() << "Downloaded " << bytesReceived << " of " << bytesTotal; double percentComplete = 0.0; if (bytesTotal > 0) { percentComplete = (bytesReceived * 100.0) / bytesTotal; qDebug() << "Percent complete: " << percentComplete; } else { qDebug() << "Total file size unavailable, downloaded:" << bytesReceived; } });
2. 完善错误处理逻辑
在finished信号中,出错时删除已创建的空文件:
connect(reply, &QNetworkReply::finished, [this, reply]() { QString fileName = "C:/Users/Raja/Downloads/file.zip"; if (reply->error() != QNetworkReply::NoError) { qDebug() << "Final error: " << reply->errorString(); QFile::remove(fileName); } else { QFile file(fileName); if (file.open(QIODevice::WriteOnly)) { QByteArray data = reply->readAll(); qint64 bytesWritten = file.write(data); file.close(); if (bytesWritten == data.size()) { qDebug() << "File downloaded successfully, total size:" << bytesWritten; } else { qDebug() << "Error: Partial data written"; QFile::remove(fileName); } } else { qDebug() << "Error: Unable to open file for writing"; } } reply->deleteLater(); });
3. 监听error信号提前捕获异常
额外连接error信号,在错误发生时立即处理:
connect(reply, &QNetworkReply::errorOccurred, [this](QNetworkReply::NetworkError code, const QString& msg) { qDebug() << "Download error: code" << code << ", message:" << msg; QString fileName = "C:/Users/Raja/Downloads/file.zip"; QFile::remove(fileName); });
4. 显式启用重定向跟随(可选)
确保请求允许跟随重定向,避免潜在的链接跳转问题:
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setAttribute(QNetworkRequest::MaximumRedirectsAllowedAttribute, 5); // 限制最大重定向次数
修复后完整代码
QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkRequest request; request.setUrl(QUrl("https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n5.0.2.zip")); // 显式设置重定向跟随 request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setAttribute(QNetworkRequest::MaximumRedirectsAllowedAttribute, 5); QNetworkReply *reply = manager->get(request); // 监听下载进度 connect(reply, &QNetworkReply::downloadProgress, [this](qint64 bytesReceived, qint64 bytesTotal) { qDebug() << "Downloaded " << bytesReceived << " of " << bytesTotal; double percentComplete = 0.0; if (bytesTotal > 0) { percentComplete = (bytesReceived * 100.0) / bytesTotal; qDebug() << "Percent complete: " << percentComplete; } else { qDebug() << "Total file size unavailable, downloaded:" << bytesReceived; } }); // 监听错误信号 connect(reply, &QNetworkReply::errorOccurred, [this](QNetworkReply::NetworkError code, const QString& msg) { qDebug() << "Download error: code" << code << ", message:" << msg; QString fileName = "C:/Users/Raja/Downloads/file.zip"; QFile::remove(fileName); }); // 监听完成信号 connect(reply, &QNetworkReply::finished, [this, reply]() { QString fileName = "C:/Users/Raja/Downloads/file.zip"; if (reply->error() != QNetworkReply::NoError) { qDebug() << "Final error: " << reply->errorString(); } else { QFile file(fileName); if (file.open(QIODevice::WriteOnly)) { QByteArray data = reply->readAll(); qint64 bytesWritten = file.write(data); file.close(); if (bytesWritten == data.size()) { qDebug() << "File downloaded successfully, total size:" << bytesWritten; } else { qDebug() << "Error: Partial data written"; QFile::remove(fileName); } } else { qDebug() << "Error: Unable to open file for writing"; } } reply->deleteLater(); });
内容的提问来源于stack exchange,提问作者Cesar
相关产品推荐
相关产品推荐

