Qt之HTTP上传/下载
扫描二维码
随时随地手机看文章
简述
在前面章节中我们讲述了关于Qt显示网络图片的内容,比较简单,因为图片一般都比较小,下载到本地速度比较快,所以基本不需要什么特殊处理,本节我们主要针对HTTP实现上传/下载进行详细的讲解与分享,包括:用户认证,实时获取下载大小、速度、剩余时间信息等。
首先看一下即将用到的公式:
文件剩余大小 = 文件总大小 - 文件已下载大小
平均速度 = 文件已下载大小 / 文件已下载大小所用的时间
瞬时速度 = 每秒下载的文件大小
剩余时间 = 文件剩余大小 / 瞬时速度
下面以下载为例,来实现一个文件下载管理器。
简述效果QNetworkAccessManager使用转换总结更多参考
效果
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来命名,当然更严格的最好再加上随机数,这样基本就不会出现重名情况。
下载时,首先判断本地文件中是否存在与下载文件同名的文件,如果有则删除,开始下载。当下载完成时,需要对临时文件重新命名。
以上内容比较详细,介绍了如何进行用户认证,如何实时获取下载大小、速度、剩余时间等信息,后面我们会针对断点续传来进行详细讲解,敬请期待!