Using QNetworkReply of a previous QNAM request as QHttpMultiPart body - c++

In my application I need to post a large file which is available by HTTP location to another HTTP location without loading the whole file to memory.
I've already to tried to pass QNetworkReply received from the previous QNAM request instead of QFile, but no luck: the code compiles and starts downloading very_large_file.bin with no problems, but there does no POST request occurs:
QNetworkRequest request(QUrl("http://mylocation/very_large_file.bin"));
QNetworkReply *reply = nm.post(request, &multipart);
QHttpMultiPart multipart(QHttpMultiPart::FormDataType);
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("application/octet-stream"));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"myfile\"; filename=\"test.bin\""));
filePart.setBodyDevice(reply); // working fine if I pass QFile instead of QNetworkReply
QNetworkRequest request(QUrl("http://myanotherlocation/post.php"));
QNetworkReply *reply = nm.post(request, &multipart);
By searching some info in Google I've found that the problem is that I'm trying to pass a sequential QIODevice while for some reasons QNAM is only working good with non-sequential QIODevice-s, so I need to implement a wrapper that converts QNetworkReply to something that QNAM would understand.
Does anybody can provide me with a minimal working example of that? It's necessary that very_large_file.bin will be transfered from mylocation to myanotherlocation chunk-by-chunk, rather from being fully loaded to memory.

Related

QNetworkAccessManager and cookies

I'm trying to download a website (youtube) that opens after user passes the consent page (accepting cookies). So i create QNetworkRequest request and set RawHeader to ("COOKIE" , "CONSENT=YES+42"). It works fine, but only with the first attempt to download. With every next attempt i bounce against the consent page. The problem is somehow bypassed when each time i use deleteLater() on QNetworkAccessManager object. But the documentation claims "One QNetworkAccessManager instance should be enough for the whole Qt application" (also creating new instance of QNetworkAccessManager for each use eventually results with not receiving a replay and rise of processor use). So my question is how to "reset" QNetworkAccessManager so with each next use, it acts as with the first request.
My code looks like this:
Youtube::Youtube(QObject *parent) : QObject(parent)
{
manager = new QNetworkAccessManager(this);
}
void Youtube::makeRequest(QString indexCore){
QNetworkReply *reply;
QNetworkRequest request;
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReadyRead(QNetworkReply*)));
request.setRawHeader("COOKIE" , "CONSENT=YES+42" ); //works
request.setUrl(QUrl("https://" + indexCore ));
reply = manager->get(request);
}
void Youtube::slotReadyRead(QNetworkReply *replay)
{
QByteArray dataTemp = replay->readAll();
website = dataTemp.toStdString();
replay->deleteLater();
}
OK.I just had to set and empty QNetworkCookieJar.

QT: How to download from url while pressing a button

I have this code:
QNetworkAccessManager man;
QNetworkRequest req(QUrl("URL"));
QString ua("HttpRequestDemo/0.1 (Win64) Qt/5.14.0");
req.setHeader(QNetworkRequest::UserAgentHeader, QVariant(ua));
QNetworkReply* reply = man.get(req);
QObject::connect(reply){
QByteArray read = reply->readLine();
QFile out("file.txt");
out.open(QIODevice::WriteOnly|QIODevice::Text);
out.write(read);
out.close();
})
This works on the main.cpp file, using the QCoreApplication, but I want to use the QApplication and download a specific data while pressing a button.
I put the same code on the on_pushButton_clicked() in the mainwindow.cpp file and it didn't even generate the file from the url.
The problem is that man and req go out of scope and are destroyed as soon as your on_pushButton_clicked() function returns, at which point the request probably hasn't even been sent yet.
You need to make sure that these objects outlive the current scope, either by making them members of the window class, or by allocating them on the heap and setting some QObject (maybe also the window class) as the parent.
The problem is that if you put the same code in a method like X you make QNetworkAccessManager a local variable that will be removed instantly that the connection is asynchronous. The solution is to make QNetworkAccessManager an attribute of the class.
*.h
private:
QNetworkAccessManager man;
*.cpp
void Klass::on_pushButton_clicked(){
QNetworkRequest req(QUrl("URL"));
QString ua("HttpRequestDemo/0.1 (Win64) Qt/5.14.2");
req.setHeader(QNetworkRequest::UserAgentHeader, QVariant(ua));
QNetworkReply* reply = man.get(req);
connect(reply, &QNetworkReply::finished, [&]() {
QByteArray read = reply->readAll();
QFile out("file.txt");
out.open(QIODevice::WriteOnly|QIODevice::Text);
out.write(read);
out.close();
reply->close();
reply->deleteLater();
})
}
If you are planning on potentially queuing very many downloads, I strongly recommend using libcurl in your Qt app. I was using QNetworkAccessManager to down 100+ financial quote files, and it would fail downloading ~ 1/3 of the time, and take a while to download. I switched to libcurl, and after figuring out how to get my crypto root certificates setup for https, it runs much faster, and almost never fails. I run it as a dll.
And yes, you will need to make sure the network manager, whether QNetworkManager or curl, doesn't go out of scope upon exiting the button handler. A more conventional pattern, although not necessarily better, is to either have a pointer to e.g. QNetworkManager in your parent class, and new it, or use a std::unique_ptr and std::make_unique (purportedly safer). Creating large objects on the stack can cause problems (in the old days, dare I say, stack overflows), and so is usually done on the heap. In this case, it's not very big, so it doesn't really matter. Alternatively, a form creating big objects might itself be created on the heap.

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.

POST-Request with QT5

I'm new to Qt and have some difficulties regarding a post request to a PHP file and reading the response.
Everything I found about how to implement a POST request in Qt 5 is somehow outdated (Qt 4.x) and does not work properly, OR doesn't help me because of some lack of knowledge.
For example, the php file looks like this:
<?php
// read param1
$value = $_POST['param1'];
// Do some stuff here
// return some text
echo $value;
?>
All I want to do is this:
Make a post-request and deliver some data (param1, value1)
Read the return-value of the PHP file
Is there a small example of c++-code, how to implement this task with QT5?
Did you try QNetworkAccessManager?
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->post(QNetworkRequest(QUrl("http://example.com/yourscript.php")), data);
data is a QByteArray that you can generate from a QString if needed.

Web service in blackberry 10

I am developing a BlackBerry 10 apps with Cascades (C++ programming language) right now. Can anyone tell me how do i make a call to web service in BlackBerry 10: Cascades? I'm just a beginner, so i don't really know anything. Thanks for your answer
void GetWeb::start(const QString &str)
{
QNetworkRequest request = QNetworkRequest();
request.setUrl(QUrl(str));
QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager(this);
connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
networkAccessManager->get(request);
}
void GetWeb::requestFinished(QNetworkReply* reply)
{
if (reply->error() == QNetworkReply::NoError)
{
emit complete(reply->readAll());
}
reply->deleteLater();
}
In this case I am emiting the resulting string as a signal, but you could also just use the reply->readAll() string directly if you wished...
There's a few moving parts to sending a network request using Qt. Here's the example Qt uses:
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
So what you do is create a QNetworkAccessManager object, which handles the actual process of sending the request and processing the response. You then connect the signal that the manager emits when the QNetworkRequest has finished to a slot you've created called replyFinished which takes QNetworkReply * as a parameter, that might look like this:
void MyClass::replyFinished(QNetworkReply *serverResponse)
{
//do something with the response
}
You then use the managers get method to pass your QNetworkRequest, which you can create like it has been there, or separately. And that's about it, that's a minimal example that'll send a HTTP request to http://qt-project.org and return a response containing the data from the page, you can extend out from there to do things like get JSON or XML.
Example from: QtNetwork documentation