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来命名,当然更严格的最好再加上随机数,这样基本就不会出现重名情况。
下载时,首先判断本地文件中是否存在与下载文件同名的文件,如果有则删除,开始下载。当下载完成时,需要对临时文件重新命名。
以上内容比较详细,介绍了如何进行用户认证,如何实时获取下载大小、速度、剩余时间等信息,后面我们会针对断点续传来进行详细讲解,敬请期待!
Qt之HTTP上传/下载的更多相关文章
- Qt之HTTP上传/下载(继承QNetworkAccessManager,包括使用了authenticationRequired认证信号)
效果 QNetworkAccessManager DownloadNetworkManager::DownloadNetworkManager(QObject *parent) : QNetworkA ...
- Selenium2学习-039-WebUI自动化实战实例-文件上传下载
通常在 WebUI 自动化测试过程中必然会涉及到文件上传的自动化测试需求,而开发在进行相应的技术实现是不同的,粗略可划分为两类:input标签类(类型为file)和非input标签类(例如:div.a ...
- java客户端调用ftp上传下载文件
1:java客户端上传,下载文件. package com.li.utils; import java.io.File; import java.io.FileInputStream; import ...
- Struts的文件上传下载
Struts的文件上传下载 1.文件上传 Struts2的文件上传也是使用fileUpload的组件,这个组默认是集合在框架里面的.且是使用拦截器:<interceptor name=" ...
- 基于Spring Mvc实现的Excel文件上传下载
最近工作遇到一个需求,需要下载excel模板,编辑后上传解析存储到数据库.因此为了更好的理解公司框架,我就自己先用spring mvc实现了一个样例. 基础框架 之前曾经介绍过一个最简单的spring ...
- Android okHttp网络请求之文件上传下载
前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...
- 用Canvas+Javascript FileAPI 实现一个跨平台的图片剪切、滤镜处理、上传下载工具
直接上代码,其中上传功能需要自己配置允许跨域的文件服务器地址~ 或者将html文件贴到您的站点下同源上传也OK. 支持: 不同尺寸图片获取. 原图缩小放大. 原图移动. 选择框大小改变. 下载选中的区 ...
- Javaweb学习笔记——上传下载文件
一.前言 在Javaweb中,上传下载是经常用到的功能,对于文件上传,浏览器在上传的过程中是以流的过程将文件传给服务器,一般都是使用commons-fileupload这个包实现上传功能,因为comm ...
- 服务器文件上传下载(XShell+Xftp)
1.下载XShell安装包+Xftp安装包.百度网盘(XShell):https://pan.baidu.com/s/1eR4PFpS 百度网盘(Xftp):https://pan.baidu.com ...
随机推荐
- 安装hbase-0.98.9-hadoop2
1. download http://124.202.164.13/files/1244000005C563FC/www.eu.apache.org/dist/hbase/stable/hbase-0 ...
- jQuery1.9.1--结构及$方法的工作原理源码分析
jQuery的$方法使用起来非常的多样式,接口实在太灵活了,有点违反设计模式的原则职责单一.但是用户却非常喜欢这种方式,因为不用记那么多名称,我只要记住一个$就可以实现许多功能,这个$简直就像个万能的 ...
- 用 VIPER 构建 iOS 应用架构(2)
[编者按]本篇文章由 Jeff Gilbert 和 Conrad Stoll 共同编写,通过构建一个基础示例应用,深入了解 VIPER,并从视图.交互器等多个部件理清 VIPER 的整体布局及思路.通 ...
- 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(3)
[编者按]本篇文章作者是 Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于 Parse 特点,打造一款类似 Instagr ...
- VS2012 win7 修改TFS登陆账号
1.修改登陆账号: 在网上搜了好多,都没有找到解决方法,自己琢磨了一会找到了修改登陆TFS(Team Foundation Server)(团队资源管理器)的账号,和大家分享一下吧. 点击“开始”-- ...
- 谈JavaScript组合拼接字符串的效率 --转载
JavaScript组合拼接字符串的效率.在脚本开发过程中,经常会按照某个规则,组合拼接出一个大字符串进行输出.比如写脚本控件时控制整个控件的外观的HTML标签输出,比如AJAX里得到服务器端的回传值 ...
- ios 团购分类页面(9宫格)
=-= 命名有些错误,但功能实现,以后注意下命名规范 WJViewGroup.h #import <UIKit/UIKit.h> @interface WJViewGroup : UIVi ...
- MongoDB 安装,启动与基本使用
一.MongoDB简介 MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.它在许多场景下可用于替代传统的关系型数据库或键/值存储方式.Mongo使用C++ ...
- CSS布局:让页底内容永远固定在底部
我们在设计一些页面内容甚少的网页时(典型应用就是登陆页面),由于显示器的分辨率大,在正常情况下,假如页面内容高度小于浏览器高度时,页面底部以下会留下很大的空间... 先看示例:http://www.h ...
- Heap和Heapify
最近复习数据结构,又回去再看塞神的课件,看到PriorityQueue的实现.自己也根据塞神的代码写一写. 下面使用Binary Heap实现了一个简单的 Max-oriented PriorityQ ...