1. 程式人生 > >Qt 之 HTTP 上傳/下載

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來命名,當然更嚴格的最好再加上隨機數,這樣基本就不會出現重名情況。

下載時,首先判斷本地檔案中是否存在與下載檔案同名的檔案,如果有則刪除,開始下載。當下載完成時,需要對臨時檔案重新命名。

以上內容比較詳細,介紹瞭如何進行使用者認證,如何實時獲取下載大小、速度、剩餘時間等資訊,後面我們會針對斷點續傳來進行詳細講解,敬請期待!

更多參考