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.
Related
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.
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.
I am writing an application to request a web page at equal intervals in order to get any changes in it (to check whether new data is received). here how i did it.
private:
QNetworkReply *r;
QNetworkAccessManager *m;
QNetworkRequest request;
QTimer *timer;
in the constructor ,
m = new QNetworkAccessManager(this);
timer = new QTimer(this);
connect(r , SIGNAL(readyRead()), this , SLOT(readit()));
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(1000);
readit function,
void MainWindow::readit(){
QString st;
st=r->readAll();
m->deleteResource(request);
ui->textBrowser->append(st);
}
update function,
void MainWindow::update()
{
request.setUrl(QUrl("http://localhost/test/default.php"));
r = m->get(request);
}
my problem is m->get(request) gets the request at its first call only, when it is called again it does nothing. I did several experiments but end up with no success results. i changed the second request to another web page using a button click but it did nothing too.
So I need help from an expert how to update the get request and get new reply multiple times.
and also i want to know am i doing a correct thing or is there mo reliable methods to get data on data change from the server than checking for the website at regular intervals.
I see following problems:
readyRead fires an arbitrary number of times per request - including zero times (!), but you treat it as if it fired exactly once. Use the finished signal, which is does what you want: fires once, no more, no less.
The update slot doesn't connect any slots to the request.
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
Im making a connection every X sec. but in case net bandwidth is overloaded timer fires before QNetworkAccessManager sends finished signal and app crashes.
MainWindow::construct:
pTimer = new QTimer(this);
connect(pTimer, SIGNAL(timeout()), this, SLOT(connect()));
pTimer->start(5000);
MainWindow::connect()
pNetworkManager = new QNetworkAccessManager(this);
connect(pNetworkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(result(QNetworkReply*)));
pNetworkManager->get(QNetworkRequest(url));
MainWindow::result(QNetworkReply *reply) processes the response
how to check if QNetworkAccessManager isFinished before timer fires again?
Don't create a network manager per request, but only one network manager for your class. Otherwise you leak managers with each request until the mainwindow is destroyed.
QNetworkManager::get returns the pointer to the QNetworkReply representing the request. You can store that reply in a QPointer, connect to its signals, check QNetworkReply::isFinished() etc. to track whether the request is still running or not.
Why not simply launch timer after request finished?
pTimer = new QTimer(this);
pTimer->setSingleshot(true);
connect(pTimer, SIGNAL(timeout()), this, SLOT(connect()));
pTimer->start(5000);
pNetworkManager = new QNetworkAccessManager(this);
connect(pNetworkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(result(QNetworkReply*)));
connect(pNetworkManager, SIGNAL(finished(QNetworkReply*)), pTimer, SLOT(start()));