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

如何用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();
});

问题原因

  1. bytesTotal为-1的本质:该值表示服务器未返回Content-Length响应头,或请求过程中发生网络错误(如连接中断、重定向异常)。
  2. 非法数值计算:代码直接用bytesTotal做除法,当它为-1时会产生无效的百分比值。
  3. 错误处理不完整:下载出错时未清理已创建的空文件,且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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 09:35:22