Qt Can't download file using QNetworkAccessManager - c++

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();

Related

Using QNetworkReply of a previous QNAM request as QHttpMultiPart body

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.

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();

QNetworkRequest and QUrl encoding c++

Im trying to connect to gmail and consume the atom file.
Im having problem with passwords that contain !#$%^&* chars.
pNetworkManager = new QNetworkAccessManager(this);
connect(pNetworkManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(result(QNetworkReply*)));
Settings settings;
settings.load();
QString url;
url.append("https://");
url.append(settings.getUserName());
url.append(":");
url.append(QUrl::toPercentEncoding(settings.getPassword()));
url.append("#mail.google.com/mail/feed/atom");
pNetworkManager->get(QNetworkRequest(QUrl(url.toUtf8())));
I get reply "Protocol "" is unknown"
Qt 4.8
How is this done properly
Why don't you construct a QUrl directly?
QUrl url("https://mail.google.com/mail/feed/atom");
url.setUserName(settings.getUserName());
url.setPassword(settings.getPassword());
pNetworkManager->get(QNetworkRequest(url));
It handles all the necessary encoding and QNetworkRequest takes it directly anyway. No need to mess with string encodings.

Writing a QNetworkReply to a file

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;