• Qt之HTTP上传/下载


    简述

    在前面章节中我们讲述了关于Qt显示网络图片的内容,比较简单,因为图片一般都比较小,下载到本地速度比较快,所以基本不需要什么特殊处理,本节我们主要针对HTTP实现上传/下载进行详细的讲解与分享,包括:用户认证,实时获取下载大小、速度、剩余时间信息等。

    首先看一下即将用到的公式:

    文件剩余大小 = 文件总大小 - 文件已下载大小
    平均速度 = 文件已下载大小 / 文件已下载大小所用的时间
    瞬时速度 = 每秒下载的文件大小
    剩余时间 = 文件剩余大小 / 瞬时速度

    下面以下载为例,来实现一个文件下载管理器。

    效果

    这里写图片描述

    QNetworkAccessManager

    DownloadNetworkManager::DownloadNetworkManager(QObject *parent)
        : QNetworkAccessManager(parent)
    {
        // 获取当前的时间戳,设置下载的临时文件名称
        QDateTime dateTime = QDateTime::currentDateTime();
        QString date = dateTime.toString("yyyy-MM-dd-hh-mm-ss-zzz");
        m_strFileName = QString("E:/%1.tmp").arg(date);
    
        connect(this, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
    }
    
    DownloadNetworkManager::~DownloadNetworkManager()
    {
        // 终止下载
        if (m_pReply != NULL)
        {
            m_pReply->abort();
            m_pReply->deleteLater();
        }
    }
    
    // 设置URL及消息头,开始请求
    void DownloadNetworkManager::execute()
    {
        m_url = QUrl("http://192.168.*.*/download/2.0.0.zip");
    
        QNetworkRequest request;
        request.setUrl(m_url);
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/zip");
    
        connect(this, SIGNAL(authenticationRequired(QNetworkReply *, QAuthenticator *)), this, SLOT(onAuthenticationRequest(QNetworkReply *, QAuthenticator *)));
    
        m_pReply = get(request);
        connect(m_pReply, SIGNAL(downloadProgress(qint64, qint64)), this, SIGNAL(downloadProgress(qint64, qint64)));
        connect(m_pReply, SIGNAL(readyRead()), this, SLOT(readyRead()));
    }
    
    void DownloadNetworkManager::replyFinished(QNetworkReply *reply)
    {
        // 获取响应的信息,状态码为200表示正常
        QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    
        // 无错误返回
        if (reply->error() == QNetworkReply::NoError)
        {
            // 重命名临时文件
            QFileInfo fileInfo(m_strFileName);
            QFileInfo newFileInfo = fileInfo.absolutePath() + m_url.fileName();
            QDir dir;
            if (dir.exists(fileInfo.absolutePath()))
            {
                if (newFileInfo.exists())
                    newFileInfo.dir().remove(newFileInfo.fileName());
                QFile::rename(m_strFileName, newFileInfo.absoluteFilePath());
            }
        }
        else
        {
            QString strError = reply->errorString();
            qDebug() << "Error:" << strError;
        }
    
        emit replyFinished(statusCode.toInt());
    }
    
    // 用户认证
    void DownloadNetworkManager::onAuthenticationRequest(QNetworkReply *reply, QAuthenticator *authenticator)
    {
        QByteArray password;
        password.append("123456");
        password = QByteArray::fromBase64(password);
    
        QString strPassword(password);
    
        authenticator->setUser("wang");
        authenticator->setPassword(strPassword);
    }
    
    // 本地写文件
    void DownloadNetworkManager::readyRead()
    {
        QFileInfo fileInfo(m_strFileName);
        QFileInfo newFileInfo = fileInfo.absolutePath() + m_url.fileName();
        QString strFileName = newFileInfo.absoluteFilePath();
    
        emit fileName(strFileName);
    
        // 写文件-形式为追加
        QFile file(m_strFileName);
        if (file.open(QIODevice::Append))
            file.write(m_pReply->readAll());
        file.close();
    }

    使用

    调用download()接口开始下载,关联downloadProgress信号和槽,可以实时获取下载大小、速度、剩余时间等信息。

    // 开始下载
    void MainWindow::download()
    {
        if (m_pNetworkManager == NULL)
        {
            m_pNetworkManager = new DownloadNetworkManager(this);
            connect(m_pNetworkManager, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64)), Qt::QueuedConnection);
            connect(m_pNetworkManager, SIGNAL(replyFinished(int)), this, SLOT(replyFinished(int)), Qt::QueuedConnection);
            connect(m_pNetworkManager, SIGNAL(fileName(QString)), m_pFileInfoLabel, SLOT(setText(QString)), Qt::QueuedConnection);
        }
        m_pNetworkManager->execute();
        downloadTime.start();
    }
    
    // 计算下载大小、速度、剩余时间
    void MainWindow::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
    {
        // 总时间
        int nTime = downloadTime.elapsed();
    
        // 本次下载所用时间
        nTime -= m_nTime;
    
        // 下载速度
        double dBytesSpeed = (bytesReceived * 1000.0) / nTime;
        double dSpeed = dBytesSpeed;
    
        //剩余时间
        qint64 leftBytes = (bytesTotal - bytesReceived);
        double dLeftTime = (leftBytes * 1.0) / dBytesSpeed;
    
        m_pSpeedInfoLabel->setText(speed(dSpeed));
        m_pLeftTimeInfoLabel->setText(timeFormat(qCeil(dLeftTime)));
        m_pFileSizeInfoLabel->setText(size(bytesTotal));
        m_pDownloadInfoLabel->setText(size(bytesReceived));
        m_pProgressBar->setMaximum(bytesTotal);
        m_pProgressBar->setValue(bytesReceived);
    
        // 获取上一次的时间
        m_nTime = nTime;
    }
    
    // 下载完成
    void MainWindow::replyFinished(int statusCode)
    {
        m_nStatusCode = statusCode;
        QString strStatus = (statusCode == 200) ? QStringLiteral("下载成功") : QStringLiteral("下载失败");
        m_pStatusLabel->setText(strStatus);
    }

    转换

    下面是一些数据的格式转换,包括:字节转KB、MB、GB,速度转KB/S、MB/S、GB/S,秒转*d *h *m *s格式。

    // 字节转KB、MB、GB
    QString size(qint64 bytes)
    {
        QString strUnit;
        double dSize = bytes * 1.0;
        if (dSize <= 0)
        {
            dSize = 0.0;
        }
        else if (dSize < 1024)
        {
            strUnit = "Bytes";
        }
        else if (dSize < 1024 * 1024)
        {
            dSize /= 1024;
            strUnit = "KB";
        }
        else if (dSize < 1024 * 1024 * 1024)
        {
            dSize /= (1024 * 1024);
            strUnit = "MB";
        }
        else
        {
            dSize /= (1024 * 1024 * 1024);
            strUnit = "GB";
        }
    
        return QString("%1 %2").arg(QString::number(dSize, 'f', 2)).arg(strUnit);
    }
    
    // 速度转KB/S、MB/S、GB/S
    QString speed(double speed)
    {
        QString strUnit;
        if (speed <= 0)
        {
            speed = 0;
            strUnit = "Bytes/S";
        }
        else if (speed < 1024)
        {
            strUnit = "Bytes/S";
        }
        else if (speed < 1024 * 1024)
        {
            speed /= 1024;
            strUnit = "KB/S";
        }
        else if (speed < 1024 * 1024 * 1024)
        {
            speed /= (1024 * 1024);
            strUnit = "MB/S";
        }
        else
        {
            speed /= (1024 * 1024 * 1024);
            strUnit = "GB/S";
        }
    
        QString strSpeed = QString::number(speed, 'f', 2);
        return QString("%1 %2").arg(strSpeed).arg(strUnit);
    }
    
    // 秒转*d *h *m *s
    QString timeFormat(int seconds)
    {
        QString strValue;
        QString strSpacing(" ");
        if (seconds <= 0)
        {
            strValue = QString("%1s").arg(0);
        }
        else if (seconds < 60)
        {
            strValue = QString("%1s").arg(seconds);
        }
        else if (seconds < 60 * 60)
        {
            int nMinute = seconds / 60;
            int nSecond = seconds - nMinute * 60;
    
            strValue = QString("%1m").arg(nMinute);
    
            if (nSecond > 0)
                strValue += strSpacing + QString("%1s").arg(nSecond);
        }
        else if (seconds < 60 * 60 * 24)
        {
            int nHour = seconds / (60 * 60);
            int nMinute = (seconds - nHour * 60 * 60) / 60;
            int nSecond = seconds - nHour * 60 * 60 - nMinute * 60;
    
            strValue = QString("%1h").arg(nHour);
    
            if (nMinute > 0)
                strValue += strSpacing + QString("%1m").arg(nMinute);
    
            if (nSecond > 0)
                strValue += strSpacing + QString("%1s").arg(nSecond);
        }
        else
        {
            int nDay = seconds / (60 * 60 * 24);
            int nHour = (seconds - nDay * 60 * 60 * 24) / (60 * 60);
            int nMinute = (seconds - nDay * 60 * 60 * 24 - nHour * 60 * 60) / 60;
            int nSecond = seconds - nDay * 60 * 60 * 24 - nHour * 60 * 60 - nMinute * 60;
    
            strValue = QString("%1d").arg(nDay);
    
            if (nHour > 0)
                strValue += strSpacing + QString("%1h").arg(nHour);
    
            if (nMinute > 0)
                strValue += strSpacing + QString("%1m").arg(nMinute);
    
            if (nSecond > 0)
                strValue += strSpacing + QString("%1s").arg(nSecond);
        }
    
        return strValue;
    }

    总结

    一般来说,我们下载文件到本地,需要设置一个临时文件名,这里我以时间戳为名称外加.tmp来命名,当然更严格的最好再加上随机数,这样基本就不会出现重名情况。

    下载时,首先判断本地文件中是否存在与下载文件同名的文件,如果有则删除,开始下载。当下载完成时,需要对临时文件重新命名。

    以上内容比较详细,介绍了如何进行用户认证,如何实时获取下载大小、速度、剩余时间等信息,后面我们会针对断点续传来进行详细讲解,敬请期待!

  • 相关阅读:
    linux常用的一些访问目录
    专业术语
    java基础知识回顾之javaIO类--File类应用:获取指定目录下面的指定扩展名的文件,将文件的绝对路径写入到目的文件当中
    java基础知识回顾之javaIO类--File类应用:删除带内容的目录
    java基础知识回顾之javaIO类--File类应用:过滤器接口FilenameFilter和FileFilter
    java基础知识回顾之javaIO类--File类应用:递归深度遍历文件
    java基础知识回顾之javaIO类--File类
    java基础知识回顾之java集合类-Properties集合
    java基础知识回顾之---java StringBuffer,Stringbuilder与String的区别
    java基础知识回顾之---java StringBuilder与StringBuffer异同点
  • 原文地址:https://www.cnblogs.com/itrena/p/5938371.html
Copyright © 2020-2023  润新知