News
17th December 2014 | By:

Downloading files with QNetworkaccessManager

Downloading a file via HTTP is really easy using Qt5, everything is done through the QNetworkaccessManager, QNetworkReply and QNetworkRequest objects. In the following article we will try to shed some light on how to use the components to efficiently download a file via HTTP.

Simple example

Before being able to use the network components you will need to amend your .PRO file and append the following:

QT += network

Once you have done that you will have access to the network components, now let’s start with a simple program.

#include <QtGlobal>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QUrl>
void downloadProgress(quint64 current_position, quint64 total_size){
	/*...*/
}
void downloadFinished(){
	/*...*/
}
void downloadReadyRead(){
	/*...*/
}
void error(QNetworkReply::NetworkError err){
	/*...*/
}
// the main QNetworkAccessManager object which is responsible of creating and managing connections.
QNetworkAccessManager m_network_manager;
// debian's netinstall iso URL, notice the protocol used is HTTP.
QUrl url("http://cdimage.debian.org/debian-cd/7.7.0/amd64/iso-cd/debian-7.7.0-amd64-netinst.iso");
// create a request object which describes what we want to download
QNetworkRequest request(url);
// make the HTTP GET request
QNetworkReply *m_network_reply = m_network_manager.get(request);
// connect signals
connect(m_network_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(downloadProgress(qint64,qint64)));
connect(m_network_reply, SIGNAL(finished()), SLOT(downloadFinished()));
connect(m_network_reply, SIGNAL(readyRead()), SLOT(downloadReadyRead()));
connect(m_network_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(error(QNetworkReply::NetworkError)));

Signals

There are 4 main signals that can be used to track the QNetworkReply activity.

downloadProgress()

downloadProgress() will be triggered to notify you about the progress of the download, in conjuction it will provide you 2 arguments (quint64, quint64), first argument being the current position of the download in bytes, the second being the total size of the download. This can be very useful when trying to build a progress bar. downloadProgress() is not guaranteed to be always accurate, this heavily depends on the server sending you the total size of the resource you being downloaded. For instance when downloading a dynamically generated content via Python/PHP, the total size will be unknown (0).

readyRead()

readyRead() will be called each time there are bytes downloaded and ready for you to process them, this signal can be used to save to content into a file gradually rather than waiting the whole file to be downloaded (into memory) then writing it into disk.

A simple handler would look like this:

void downloadReadyRead(){
	if(m_network_reply->error() == QNetworkReply::NoError){
		QByteArray buf = m_network_reply->readAll();
		// do other operations such as writing it to a file.
		// you can also use the m_network_reply->bytesAvailable() in order to find out how many bytes are available.
	}
}

finished()

The finished() signal is triggered whenever the download is considered to be finished, when finished() is called it does not mean that the download has been downloaded, you need to ensure that there was no error and the download was indeed finished in full. For instance calling abort() will also call finished(), safe way to consider that a download is finished to use something like this:

void downloadReadyRead(){
	if(m_network_reply->error() == QNetworkReply::NoError){
		// there was no error, should be safe
	}else{
		// handle an error...
	}
}

Canceling download

Canceling/Aborting a download is very easy, all you have to do is call the abort() method of QNetworkReply object. This will make sure the current download in progress is canceled. Aborting a download also triggers the error() finished() and readyRead() signals so make sure you handle the signals properly.

Limiting the bandwidth

Limiting the bandwidth can be done via setReadBufferSize(), unfortunately in Qt5 there is currently a bug (https://bugreports.qt-project.org/browse/QTBUG-15065) which prevents this. The idea is to use a limit the buffer size so you can control how many bytes are read from the network.

References
QNetworkAccessManager: http://doc.qt.io/qt-5/qnetworkaccessmanager.html
QNetworkRequest: http://doc.qt.io/qt-5/qnetworkrequest.html
QNetworkReply: http://doc.qt.io/qt-5/qnetworkreply.html
Media: http://www.thelinuxterminal.com/

Tags: , , ,