Qt使用QNetworkAssessManager下载文件

0 篇文章 0 订阅

功能目录:

  1. QNetworkAccessManager接口介绍
  2. 下载进度条展示,支持暂停,停止功能
  3. 显示下载/剩余大小,剩余时间,下载速度
  4. 多线程下载,不阻塞界面线程
  5. 文件断点续传下载
  6. 下载请求超时的处理
  7. 功能实现Demo地址

1.QNetworkAccessManager接口介绍

    官方文档:http://doc.qt.io/archives/qt-5.8/qnetworkaccessmanager.html

    可以一目了然的看到几个熟悉词汇的api:post、get、put、head,当然还有几个cookie相关的方法。

    可以发现使用manager还需要几个类:QNetworkRequest 专门用于请求的,QNetworkReply 接收请求的响应.

     比如1.只想从网页服务器上请求数据,使用get方法。         

        QNetworkAccessManager *manager = new QNetworkAccessManager(this);
        connect(manager, SIGNAL(finished(QNetworkReply*)),
                this, SLOT(replyFinished(QNetworkReply*)));
        QNetworkRequest request;
        request.setUrl(QUrl("http://qt-project.org"));
       
        reply = manager->get(request);
        connect(reply, &QNetworkReply::finished, this, &MainWindow::finished);

        //在finished函数中
        QByteArray bytes = reply->readAll();//读取网页数据

      2.附带数据向服务器请求返回数据,使用post,当post附带json数据时:

    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QNetworkRequest request;

    //构造json数据
    QJsonObject jsonObject;
    jsonObject.insert("dcode", "code");
    jsonObject.insert("ver", "version");

    //json转QByteArray
    QString strJson = QString(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact));
    QByteArray byte_json = strJson.toLocal8Bit();
    
    request.setUrl(QUrl("http://qt-project.org"));//要请求的地址
    //如果请求json数据,请求header要使用此设置
    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
    
    if (m_Reply != Q_NULLPTR) {
		m_Reply->deleteLater();
	}
	m_Reply = manager->post(request, byte_json);
    QEventLoop loop;//非阻塞方式,采用事件循环
	QTimer timer;
	connect(m_Reply, SIGNAL(finished()), &loop, SLOT(quit()));
	QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
	timer.start(5000);//设置超时5s
	loop.exec(QEventLoop::ExcludeUserInputEvents);

	if (m_Reply->error() == QNetworkReply::NoError){
        QByteArray  strJsonText = m_Reply->readAll();//请求返回数据
    }

    1.1. QNetworkRequest

    同样看帮助文档:http://doc.qt.io/qt-5/qnetworkrequest.html

    主要就是这几个写方法,分别对一个请求的不同类进行配置。

            客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请     求数据四个部分组成,下图给出了请求报文的一般格式。请求行组成:请求方法+空格+url+空格+协议版本+回车符+换行符。       详情见HTTP 消息结构
     对于header,qt提供了一个枚举类型KnownHeaders分别表示不同项:

ConstantValueDescription
QNetworkRequest::ContentDispositionHeader6Corresponds to the HTTP Content-Disposition header and contains a string containing the disposition type (for instance, attachment) and a parameter (for instance, filename).
QNetworkRequest::ContentTypeHeader0Corresponds to the HTTP Content-Type header and contains a string containing the media (MIME) type and any auxiliary data (for instance, charset).
QNetworkRequest::ContentLengthHeader1Corresponds to the HTTP Content-Length header and contains the length in bytes of the data transmitted.
QNetworkRequest::LocationHeader2Corresponds to the HTTP Location header and contains a URL representing the actual location of the data, including the destination URL in case of redirections.
QNetworkRequest::LastModifiedHeader3Corresponds to the HTTP Last-Modified header and contains a QDateTime representing the last modification date of the contents.
QNetworkRequest::CookieHeader4Corresponds to the HTTP Cookie header and contains a QList<QNetworkCookie> representing the cookies to be sent back to the server.
QNetworkRequest::SetCookieHeader5Corresponds to the HTTP Set-Cookie header and contains a QList<QNetworkCookie> representing the cookies sent by the server to be stored locally.
QNetworkRequest::UserAgentHeader7The User-Agent header sent by HTTP clients.
QNetworkRequest::ServerHeader8The Server header received by HTTP clients.

1.2. QNetworkReply

    帮助文档:http://doc.qt.io/qt-5/qnetworkreply.html

    此类继承自QIODevice,可使用QIODevice的所有接口,包括readall读取接收的所有信息。

    同时此类提供了finished信号,在响应完斥候发出此信号,可关联自定义槽函数函数,做响应处理。

    提供了attribute属性函数,可以判断响应的类型,比如RedirectionTargetAttribute是目标url告知进行重定向

    QNetworkReply不会自动释放空间,一定要主动处理内存释放,可以调用QObject::deleteLater令其自动释放空间

connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
connect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));

如果请求下载功能:

onDownloadProgress(qint64, qint64);显示的就是下载的进度,参数1和2分别代表本次下载字节数和文件总字节大小。

onReadyRead();下载过程中开始从服务器读取数据,用于读取数据写入本地文件中,采用数据追加的方式方便断点的续传:          

    void xx::onReadyRead()
    {
        if (m_reply == NULL) return;
        QFile file(m_fileName);//本地文件名
        if (file.open(QIODevice::WriteOnly | QIODevice::Append))
        {
            file.write(m_reply->readAll());
        }
        file.close();
    }

onFinished():下载完成事件。

onError():下载错误事件

2.下载进度条展示,支持暂停,停止功能
    
    QNetworkAccessManager使用get方法发送请求,使用QNetworkReply接受返回。QNetworkReply使用下载进程信号来反馈每次下载数据的大小和文件的总大小。
    2.1    readyRead()
        这是QNetworkReply的父类QIODevice提供的下载过程中的信号,可以在此信号中写入下载文件大小。
    2.2 downloadProgress(qint64 bytesReceived, qint64 bytesTotal) 
        bytesReceived代表每次下载文件字节大小,bytesTotal代表下载文件字节总大小。                    

//更新进度条;  
QString sValue = QString("%1").arg(bytesReceived * 100 / (bytesTotal));//百分比,所以乘以100
ui.progressBar->setValue(sValue.toInt());
        


    2.3 暂停和停止功能要点:需要解绑QNetworkReply的信号和槽,并调用QNetworkReply的abort方法和DeleteLater()方法。

 disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(onDownloadProgress(qint64, qint64)));
 disconnect(m_reply, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
 disconnect(m_reply, SIGNAL(finished()), this, SLOT(onFinished()));
 disconnect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
 m_reply->abort();
 m_reply->deleteLater();
 m_reply = NULL;

    

3.显示下载/剩余大小,剩余时间,下载速度
        计算剩余大小,剩余时间和下载速度,需要根据下载过程中每次下载字节数和总下载字节数计算,所以还是在downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
        响应的槽函数中来计算。

        // 下载大小MB/总大小MB
        QString strDownCurrent = QStringLiteral("下载大小:%1/%2").arg(transformUnit(m_currentDownload)).arg(transformUnit(bytesTotal));
        ui.lb_size->setText(strDownCurrent);

        //剩余时间
        qint64 ispeed		= m_intervalDownload * 1000 / (timeNow - m_timeInterval);
        qint64 timeRemain	= (bytesTotal - bytesReceived) / ispeed;
        QString strRemaind	= QStringLiteral("剩余时间:%1").arg(transformTime(timeRemain));
        ui.lb_remaind->setText(strRemaind);

        //下载速度
        QString strSpeed	= QStringLiteral("下载速度:%1").arg(transformUnit(ispeed, true));
        ui.lb_rate->setText(strSpeed);

4.多线程下载,不阻塞界面线程        
        此处有两种方法,一种是通过MoveToThread方法,通过一个继承与QObject的功能类实现。
        二是继承QThread,通过QThread来实现多线程,我使用的是第二种,提供过一个继承与QThread的中间类。
        当点击下载按钮时,启动下载线程:

        m_strTargetAddress  = "d:/Qt_Download.zip";//自定义目标下载地址
        QString strUrl	    = ui.lineEdit->text();//服务器下载地址

        if (m_pDownloadThread != NULL )
        {
            m_pDownloadThread->start();
        }

5.文件断点续传下载
        5.1关于断点续传,QNetworkRequest提供了方法SetRawHeader和"Range"字段来设置请求的字节大小和宽度。
        在HTTP协议请求中,如果想从文件的某一位置接受数据,就要加上Range头部,Range头部的格式有如下几种情况:
        表示头500个字节:bytes=0-499  
        表示第二个500字节:bytes=500-999  
        表示最后500个字节:bytes=-500  
        表示500字节以后的范围:bytes=500-  
        第一个和最后一个字节:bytes=0-0,-1  
        同时指定几个范围:bytes=500-600,601-999

        在发出带Range的请求后,服务器会在Content-Range头部返回当前接受的范围和文件总大小,如:

        Content-Range: 0-499/22400

        这里0-499是指当前发送的数据的范围,而22400则是文件的总大小。


        5.2要实现断点续传,只有客户端是不行的,服务器上的文件也必须支持断点续传,也就是说,在进行断点下载续传时,首先要判断要下载文件是否支持断点下载。
            windows下如何判断支持要下载文件支持断点下载:
            5.2.1 cmd下使用curl 命令加 -i 参数 和要请求的字节大小

                eg: curl -i --range 0-9 http://sqdownb.onlinedown.net/down/55412_20161201034403.zip
                如返回数据中包含Content-Range和 HTTP/1.1 206 Partial Content字节,例如执行上述命令时,返回结果如下:

    HTTP/1.1 206 Partial Content
    Date: Wed, 01 Aug 2018 03:12:14 GMT
    Content-Type: application/zip
    Content-Length: 10
    Last-Modified: Wed, 07 Dec 2016 10:13:32 GMT
    Connection: keep-alive
    ETag: "5847e0cc-78ac98"
    Content-Range: bytes 0-9/7908504

           处Content-Range: bytes 0-9/7908504 返回的字节数7908504就是文件的总字节大小。

            HTTP/1.1 206 Partial Content     代表状态值206 代表支持断点下载
           所以此处返回带有Content-Range和HTTP/1.1 206 Partial Content字节,代表此链接是支持断点下载的。

          5.2.2 Qt代码中通过QNetworkRequest发送请求"Range",通过返回值QNetworkReply的rawHeader("Content-Range")是否存在返回值来查看。

bool xx::bSupportBreak(const QUrl &url)
{
	QNetworkAccessManager manager;
	QEventLoop loop;
	QTimer timer;

	QNetworkRequest request;
	request.setUrl(url);
	request.setRawHeader("Range", "bytes=0-9");

	//发出请求,获取文件地址的头部信息;
	QNetworkReply *reply = manager.get(request);
	if (!reply)		return false;

	connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
	connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));

	timer.start(2000);
	loop.exec(QEventLoop::ExcludeUserInputEvents);

	if (reply->error() != QNetworkReply::NoError)
	{
		// 请求发生错误;
		qDebug() << reply->errorString();
		return false;
	}
	else if (!timer.isActive())
	{
		// 请求超时超时,未获取到文件信息;
		qDebug() << "Request Timeout";
		return false;
	}
	timer.stop();

	QByteArray range = reply->rawHeader("Content-Range");
	if (!QString::fromLocal8Bit(range).isEmpty())
		return true;

	return false;
}

6.下载请求超时的处理                 
    Qt5.8中不管是QNetworkAccessManager类还是QNetworkRequest类没有提供超时的机制,所以只能通过自己来计算。
        在开始下载时,通过定时器,每5s检测下载字节发生的变化值。        

_timeOut->start(5000);

        如果5s内没有发生变化,则默认说明超时,超时的原因有很多,下载过程中网络断开,或者网络连接不稳定,网卡被禁用掉等。通过定时器超时事件一直循环检测是否有网络链接,如果有网络连接则继续下载,反之下载暂停。这样的好处是下载没完成之前,网络出现波动不会影响整体文件的下载,通过轮询下载,一直到文件下载完成。
        
        不过此处我为了不让程序无节制的请求连接,如果断网超过30分钟就让程序断开请求,不再下载。        

void xx::handleTimeOut()
{
	if (m_bytesDown != m_bytesReceived) {
		m_bytesDown = m_bytesReceived;
	}
	else if (!m_bClickPause) {

		//网络断开时停止下载
		if (m_reply != NULL)
		{
			stopDownload();
			m_timeElapsed.start();
		}

		//30分钟循环超时(30分钟再次连接网络可以继续下载)
		int nElapsed = m_timeElapsed.elapsed();
		if (nElapsed >= 1 * 30 * 60 * 1000)
		{
			_timeOut->stop();
			return;
		}

		//检测网络
		bool bConnect = this->bSupportBreak(m_qStrUrl);
		if (bConnect)
		{
			_timeOut->stop();
			this->downloadFile(m_qStrUrl, m_qStrFilePath);
		}

	}
}

7.功能实现Demo地址

        环境是VS2015 + win10 64位。

        附带源码和可执行程序下载地址,同时还有编译成功的Openssl的32位和64位的文件。

        https://download.csdn.net/download/whanjim19/10576909

### 回答1: QT是一款自带网络支持的跨平台高效框架,它支持无数网络库,其中也包括SFTP(Secure File Transfer Protocol),我们可以很方便地使用QT来实现SFTP发送文件。 首先,需要导入QT的网络模块,然后创建一个QFile对象,将需要发送的文件读入到QByteArray中,然后创建一个QSshSocket对象,进行连接并认证,在连接成功后,使用SFTP协议进行文件传输。 具体的步骤如下: 1.导入QT的网络模块:#include<QSshSocket> 2.创建一个QFile对象,将待发送的文件读到QByteArray中:QByteArray fileData; QFile file(“filePath”); file.open(QFile::ReadOnly); fileData = file.readAll(); 3.创建QSshSocket对象,并进行连接和认证: QSshSocket *ssh = new QSshSocket(this); ssh->connectToHost(“hostAddress”, portNumber); ssh->login(“userName”, “password”); 4.在连接成功后,使用SFTP进行文件传输: ssh->sftpConnect(); ssh->sftpPut(fileData, “remoteFilePath”); ssh->sftpClose(); 以上便是使用QT来实现SFTP发送文件的简单过程。需要注意的是,SFTP是一种安全的文件传输协议,需要使用安全的方式进行连接和认证。此外,在进行文件传输时,需要确保文件格式和传输方式正确,尤其是在跨平台传输时需要特别注意。 ### 回答2: Qt是一个跨平台的C++应用程序框架,提供了很多网络编程相关的类和API。其中包括Qt Network模块,可以用来发送或接收各种网络数据。在这个模块中,提供了一个类QSshSocket可以用来实现SFTP(Secure File Transfer Protocol)协议。 SFTP是一种安全文件传输协议,使用SSH加密和认证机制,与FTP相比更加安全和可靠。在使用QSshSocket时,我们需要先建立一个SSH连接,然后使用SFTP子协议进行文件传输操作。 在Qt使用QSshSocket实现SFTP的步骤大致如下: 1. 创建QSshSocket对象,设置主机名、用户名、密码等连接参数。 2. 开启SSH连接,进行身份验证。 3. 使用openChannel函数创建SFTP通道。 4. 使用putFile函数将本地文件发送到远程主机。 需要注意的是,QSshSocket类中还提供了很多其他的函数和信号槽,可以用于传输过程中的错误处理、进度显示等操作。 总的来说,使用Qt实现SFTP文件传输比较方便,只需要简单几行代码即可完成,同时也可以实现较高的安全性和可靠性,适合于多种网络应用场景。 ### 回答3: Qt可以通过使用SSH协议实现SFTP文件传输。SFTP是基于SSH协议的远程文件传输协议,它使用加密通信通道来保证数据的传输安全。 为了使用SFTP传输文件Qt需要使用QProcess类启动sftp命令行工具,并通过其标准输入输出流与该命令行工具进行交互。Qt还可以使用SSH插件来启动ssh通信通道,建立和维护与远程主机的安全连接。SSH插件可以让Qt的应用程序不依赖于操作系统提供的SSH客户端程序,从而确保在不同平台上均能够可靠地实现SFTP文件传输。 在使用Qt实现SFTP文件传输时,需要注意以下几点: 1. 远程主机必须支持SSH协议和SFTP文件传输协议。 2. Qt在启动SFTP命令行工具时需要指定远程主机的IP地址、端口号、用户名和密码等信息,以便正确地建立通信连接并进行身份验证。 3. Qt在传输文件时需要将本地文件的路径和名称、远程主机上目标文件的路径和名称等信息传递给SFTP命令行工具并进行相应的操作。 总之,Qt可以方便地实现SFTP文件传输,并保证传输数据的安全和可靠。开发者只需要了解相应的API接口并编写相应的代码即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值