Writing a QNetworkReply to a file - c++

I'm downloading a file using QNetworkAccessManager::get but unlike QHttp::get there's no built-in way to directly write the response to a different QIODevice.
The easiest way would be to do something like this:
QIODevice* device;
QNetworkReply* reply = manager.get(url);
connect(reply, SIGNAL(readyRead()), this, SLOT(newData()));
and then in newData slot:
device->write(reply->readAll());
But I'm not sure if this is the right way, maybe I missed something.

That looks correct. I would use the lower-level forms of read() and write(), not the QByteArray ones, which do not properly support error handling, but other than that, it looks fine.
Are you having problems with it?

Better use the finished signal to read all the contents at the end of the download process. An example (remove the event loop and use a new slot to make it asynchronous):
QNetworkAccessManager manager;
QEventLoop loop;
QNetworkReply *reply = manager.get( request );
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QFile file( "YOUR FILE" );
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
delete reply;

Related

In Qt how can I delay a member function's return until a signal is received?

I want to use a recursive procedure to iterate through a large number of images in Qt: essentially the image is repeatedly quartered (up to a limit) and the user is asked whether the image passes or fails - ie if the image passes at large dimensions we call our function again with smaller dimensions (until we reach the limit), if it fails we return and so pass back up the hierarchy.
This approach seems to run into a roadblock with Qt's event-driven approach - I cannot see how I can pause the loop while waiting for the user input - ie there is nothing like a "wait_for_button_press" method.
I know that this sort of approach is regarded as an anti-pattern in event driven programming, but what is the alternative way that doesn't involve holding lots and lots of state on the heap (as opposed to getting it held for 'free' on the stack)?
QEventLoop maybe could help you. I start a http connection aside a timer with a timeout, all inside a thread. Then a wait for one of those had finish and return.
void MyThread::run(){
QNetworkAccessManager qnaManager;
bool isPost = false;
QUrl url(myUrl);
QNetworkRequest req(url);
QNetworkReply *reply;
req.setHeader(QNetworkRequest::ContentTypeHeader,
"application/json");
req.setHeader(QNetworkRequest::ContentLengthHeader,
QVariant(postData.size()).toString());
reply = qnaManager.get(req);
QEventLoop eventLoop;
QTimer timer;
timer.setSingleShot(true);
const int timeout = 400;
timer.start(timeout);
connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();
if (timer.isActive()){
//everything is ok
}else{
//timer elapsed, no replay
return;
}
}
For what it's worth, in the end I decided that the best route was to implement more message passing code - wait for the user input to dispatch a message. It was longer code than if I had used/had available the 'traditional' call back type paradigm, but it worked cleanly in the end.

Json QtNetworkReply to QByteArray

I need to do a request in Qt/c++ to get a JSON file, and then parse it and fill my object.
The request seems good, and "it looks like" my QtNetworkReply reply is filled.
But after many attempts, I still don't understand how can I convert it into a QbyteArray (I don't even know if it's the right thing to do...), for being able to convert it into my class.
Here's my code :
QNetworkAccessManager networkManager;
QUrl url("https://api.myjson.com/bins/uvki"); //url from a free json host
QNetworkRequest request;enter code here
request.setUrl(url);
QNetworkReply* reply = networkManager.get(request);
QByteArray reponse;
if (reply == NULL)
{
std::cout << "Damn" << std::endl;
exit(2);
}
reponse = reply->readAll();
if (reponse == NULL)
{
std::cout << "i hate you" << std::endl;
exit(1000);
}
I might have done some stupid stuff, I only have 2 days of c++
Can you tell me how I can convert my "reply" into my "reponse"?
The answer provided by #MichaelBoone is correct.
In addtion, with C++11, you can simplify the code by using Qt 5's QObject::connection syntax and a lambda function
QJsonDocument document;
QNetworkReply* pReply = networkManager.get(request);
connect(reply, &QNetworkReply::finished, [=](){
// the reply will return here
QByteArray response = pReply->readAll();
document = QJsonDocument::fromBinaryData(response);
});
Qt 5's connections syntax has the advantage of compile-time verification of the connection, which is not present when using the SIGNAL and SLOT macros.
You have to connect the finished() signal from reply object, or from the NetworkManager to get the results. You will also need to make *reply a class member, or you won't be able to access it within your handler SLOT.
QNetworkReply* reply = networkManager.get(request);
connect(reply, SIGNAL(finished()), this, SLOT(YourFunctionHere()));
void YourFunctionHere(){
//handle the data
}
QNetworkReply is a non-blocking function, like most QT Network functions, it is asynchronous. By the time you are reaching your conditional if statement to check the reply, it hasn't yet received a response from the network.
As far as handling the download afterwards, you are correct in using a QByteArray.
QByteArray QIODevice::readAll()
This is an overloaded function.
Reads all available data from the device, and returns it as a
QByteArray.
From there you use QJsonDocument.
QJsonDocument QJsonDocument::fromBinaryData(const QByteArray & data,
DataValidation validation = Validate)
Creates a QJsonDocument from data.
Edit - Sorry I don't have the reputation to comment, but I feel The answer provided by TheDarkKnight lends itself better to the one-off nature of a "Reply" and is less encumbered by having to create a new slot. lambdas are just very cool, and the compile time verification is nice.

Qt 5 C++ HTTP POST issues

I have been trying to use C++ and Qt for a HTTP POST request and after very much research this is the only way I could get the program to even compile. 95% of the examples had plenty of errors and wanted libraries that no longer exist. The issue is that when I do use this code its "reply" equals null... Is there any better way to do a HTTP POST request, and what am I doing wrong?
QEventLoop eventLoop;
QUrl myURL(QString("http://example.com/"));
QNetworkRequest request(myURL);
QNetworkAccessManager mgr;
QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit()));
QUrlQuery qu;
qu.addQueryItem("unm", aUser);
qu.addQueryItem("pwd", aPass);
QUrl params;
params.setQuery(qu);
QNetworkReply *reply = mgr.post(request, params.toEncoded());
qDebug() << "Success" <<reply->readAll();
You have to execute the event loop before reading the reply. Qt documentation says
QNetworkAccessManager has an asynchronous API.
So you have to wait till the reply is finished. That is the purpose of using an QEventLoop. you need to block the calling thread until QNetworkAccessManager emits finished(QNetworkReply*).
Everything is fine in your code, except you do not execute the QEventLoop.
Put eventLoop.exec() after sending the request.
QUrl myURL(QString("http://example.com"));
QNetworkRequest request(myURL);
QNetworkAccessManager mgr;
QUrlQuery qu;
qu.addQueryItem("unm", aUser);
qu.addQueryItem("pwd", aPass);
QUrl params;
params.setQuery(qu);
QNetworkReply *reply = mgr.post(request, params.toEncoded());
QEventLoop eventLoop;
QObject::connect(&mgr, SIGNAL(finished(QNetworkReply*)), &eventLoop, SLOT(quit()));
eventLoop.exec();
qDebug() << "Success" <<reply->readAll();

Qt Can't download file using QNetworkAccessManager

In my project, I need to download a simple .txt file. I created a function to download the file based on the url:
void MainWindow::downloadFile(const QString &url, const QString &aPathInClient)
{
QNetworkAccessManager* m_NetworkMngr = new QNetworkAccessManager(this);
QNetworkReply *reply = m_NetworkMngr->get(QNetworkRequest(QUrl(url)));
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QUrl aUrl(url);
QFileInfo fileInfo=aUrl.path();
QFile file(aPathInClient+"\\"+fileInfo.fileName());
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
delete reply;
}
When my program is ran, the file is created with the correct file name, but the file is empty. The file name can only be obtained by my program if is connect to the webpage. What am I forgetting? I get no errors on build and have included all the necessary libraries.
Thanks :)
EDIT:
All is well, my problem was my link used https instead of http.
I haven't ever used the 'write' function for writing to a QFile. Try the following approach:
QFile file(aPathInClient+"\\"+fileInfo.fileName());
file.open(QIODevice::WriteOnly);
QTextStream out(&file);
out << "This file is generated by Qt\n";
EDIT: The above was just to check whether the file is being written to correctly or not. Now since it has been verified, you can try:
out << reply->readAll();

How to connect QNetworkReply signal to progress bar in qt

In the code below:
connect(network_access_manager_, SIGNAL(finished(QNetworkReply*)),
this, SLOT(onRequestCompleted_progress(QNetworkReply *)));
network_access_manager_->get(request);
The point is that while the downloading the file via get(request) is in progress I'd like to connect the signal from QNetworkReply to progressBar but I simply don't see how am I suppose to do it? The QNetworkReply is unnamed.
Any ideas?
network_access_manager_->get(request); returns QNetworkReply*, so
QNetworkReply *reply = network_access_manager_->get(request);
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(......))
will serve.