Qt5 posting data to server using QUrl / QNetworkRequest - qt

I have a piece of code that worked in 4.8 but now I need to port it to Qt5 (beta2)
This is what should happen:
I want to post some data to a webserver the url should look like this "http://server/actions.php"
Then my fields (a "Action" -string and a "data" string(json)) should be sent to the server using post. Not encoded in the url
QUrl params;
// The data to post
QVariantMap map;
map["Title"]="The title";
map["ProjectId"]="0";
map["Parent"]="0";
map["Location"]="North pole";
map["Creator"]="You";
map["Group"]="a group";
QByteArray data = Json::serialize(map); //the map is converted to json im a QByteArray
params.addEncodedQueryItem("Data",data);
params.addQueryItem("Action", "Update");
QNetworkRequest Request(QUrl("http://server.com/actions.php"));
Request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
NetManager->post(Request,params.encodedQuery());
Now, I might not be doing this right in the first place,(It worked in 4.8) but the real problem is that addEncodedQueryItem() and addQueryItem() are now gone since Qt5 and I don't know what I should replace them with.
I have read the new docs and see the new QUrlQuery but I could not figure out on my own how to use this in my case.

I faced similar problem and used code similiar to the following in Qt5
QUrl url;
QByteArray postData;
url.setUrl("http://myurl....");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
Qstring postKey = 'city';
QString postValue = 'Brisbane';
postData.append(postKey).append("=").append(postValue).append("&");
networkManager.post(request,postData);
Hope it might be useful to rewrite your code to send http post values using Qt5

Qt5 no longer has the QUrl::encodedQuery() method. Not sure, but from the documentation it might work using QUrl::query() method instead.
Hope it helps.

QUrlQuery() helps you encode POST data.
Example in PyQt 5.4:
params = QtCore.QUrlQuery()
params.addQueryItem("username", "Вагиф Plaît")
reply = QtNetwork.QNetworkAccessManager().post(request, params.toString(QtCore.QUrl().FullyEncoded))

Related

Retrieve data from QDBusMessage in Qt

I'm developing an app which have to implement a WiFi connection mechanism (it'll be a fullscreen touchscreen app). To do that I'm using the DBus to communicate with Network Manager. I managed to successfully make it scan access points and list them in the QTableView. Next thing will be to connect to the chosen wifi network. First thing I do when trying to connect is checking if there is an existing connection to that access point already in the system so I do this:
settings = new QDBusInterface("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager/Settings", "org.freedesktop.NetworkManager.Settings", dbusConnection, this);
QDBusMessage msg = settings->call("ListConnections");
QDBusArgument arg = msg.arguments().at(0).value<QDBusArgument>();
QList<QDBusObjectPath> pathsList = qdbus_cast<QList<QDBusObjectPath>>(arg);
foreach (QDBusObjectPath p, pathsList) {
QDBusInterface *conn = new QDBusInterface("org.freedesktop.NetworkManager", p.path(), "org.freedesktop.NetworkManager.Settings.Connection", dbusConnection);
QDBusMessage msg = conn->call("GetSettings");
qDebug() << "Reply: " << msg;
}
I receive the message and can read it with qDebug(). The message is in d-bus format: a{sa{sv}} as the documentation for Network Manager says. And I have problem to parse it to any usable form. From what I understand, the equivalent Qt classes I should put the data to are:
QList<QMap<QString, QMap<QString, QVariant>>>
And I think I should do it with the QDBusArgument class but can't figure out how exactly it should be done. Can someone tell me what is the best way to do this?
Thanks.
I managed to solve my problem, it turned out to be quite easy. I created the map object and used >> operator:
QDBusArgument arg = args.at(0).value<QDBusArgument>();
QMap<QString, QMap<QString, QVariant>> map;
arg >> map;

QT 5.5 - QNetworkReply empty data

I already took at others questions but I didn't find an answer.
I have a problem to print HTML code I download with a QNetworkAccessManager.
I need to log into a website to retrieve this code.
I have a slot like this:
void Aims::slotRequestFinished(QNetworkReply* requestReply)
{
QString data = QString(requestReply->readAll());
qDebug() << data;
}
For the first two steps (connection), I can see the HTML code in the console.
The last step doesn't get any data. There is no redirection nor error.
Now, the stranger part is that when I change my code to show the page into a webview, qDebug doesn't show anything, but the code loaded is shown correctly in the webview.
void Aims::slotRequestFinished(QNetworkReply* requestReply)
{
QString data = QString(requestReply->readAll());
qDebug() << data;
ui->webView->setHtml(data);
}
Well, I can save the content into a file. But I would really like to understand why I can't see anything in qDebug

How to copy local file to QClipboard (in Gnome)?

QClipboard offers several ways to copy stuff into the clipboard. There are high level functions for standard desktop (text, pixmaps, etc), but I could not figure out how to implement the standard copy file operation. Google did not help.
Just put appropriate mime type and URL of the local file into clipboard. Docs reference.
QMimeData* mimeData = new QMimeData();
mimeData->setData("text/uri-list", "file:///C:/fileToCopy.txt");
clipboard->setMimeData(mimeData);
You can use static method QUrl::fromLocalFile to get QUrl instance to be used in mimeData->setData :
mimeData->setData("text/uri-list", QUrl::fromLocalFile("C:/fileToCopy.txt"));
Okay I found the solution to my problem. The problem is that gnome (working on linux) does its own thing. The file(s) are not stored in the text/uri-list format like N1ghtLight mentioned, but uses the special x-special/gnome-copied-files format. The following code did it:
// Get clipboard
QClipboard *cb = QApplication::clipboard();
// Ownership of the new data is transferred to the clipboard.
QMimeData* newMimeData = new QMimeData();
// Copy old mimedata
const QMimeData* oldMimeData = cb->mimeData();
for ( const QString &f : oldMimeData->formats())
newMimeData->setData(f, oldMimeData->data(f));
// Copy path of file
newMimeData->setText(_file->absolutePath());
// Copy file
newMimeData->setUrls({QUrl::fromLocalFile(_file->absolutePath())});
// Copy file (gnome)
QByteArray gnomeFormat = QByteArray("copy\n").append(QUrl::fromLocalFile(_file->absolutePath()).toEncoded());
newMimeData->setData("x-special/gnome-copied-files", gnomeFormat);
// Set the mimedata
cb->setMimeData(newMimeData);

Qt, QNetworkAccess Manager download large files fail frequently

I am using the following code to download some video files through LAN.
QString url = "http://192.168.1.100/disk/IPCAMERA/" + downloadlist[downloadID];
QNetworkRequest newRequest(url);
QString concatenated = "admin:admin";
QByteArray data = concatenated.toLocal8Bit().toBase64();
QString headerData = "Basic " + data;
newRequest.setRawHeader("Authorization", headerData.toLocal8Bit());
newRequest.setUrl(url);
reply = networkManager->get(newRequest);
connect(reply, SIGNAL(finished()), this, SLOT(refStateChanged()), Qt::UniqueConnection);
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(replyDownloadProgress(qint64, qint64)));
However, the downloading frequently fails, when the reply finished signal is triggered, it does not actually finished downloading the file but only part of it. What could I do to deal with this?
It seems you should get the file size first
QNetworkReply* reply = manager->head(newRequest);
every reply is part of the Bytearray,you must add this connect:
connect(reply, SIGNAL(readyRead()),this, SLOT(SlotReadyRead()));
void ClassName::SlotReadyRead()
{
if (_file) //you must define QFile _file........
_file->write(_reply->readAll());
}
then, you will downloading the complete file.
Good luck!

Using QHttpMultiPart with QHttpPart Binary and Form Data

I wrote a method to send a image and some form data to a Django Rest Framework Server. The following method works fine, but I would like to know if there is a way to send the form fields together in one QHttpPart, instead of creating one for each field?
Is there a better way to do this? Maybe with less code?
void MyNetwork::sendBinaryFile() {
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyfinished(QNetworkReply*)));
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart aluno, palavra_chave, latitude, latitude_ref, longitude, longitude_ref;
aluno.setHeader(QNetworkRequest::ContentDispositionHeader,QVariant("form-data; name=\"aluno\""));
aluno.setBody("some useful string");
palavra_chave.setHeader(QNetworkRequest::ContentDispositionHeader,QVariant("form-data; name=\"palavra_chave\""));
palavra_chave.setBody("some other useful string");
latitude.setHeader(QNetworkRequest::ContentDispositionHeader,QVariant("form-data; name=\"latitude\""));
latitude.setBody("0");
longitude.setHeader(QNetworkRequest::ContentDispositionHeader,QVariant("form-data; name=\"longitude\""));
longitude.setBody("0");
latitude_ref.setHeader(QNetworkRequest::ContentDispositionHeader,QVariant("form-data; name=\"latitude_ref\""));
latitude_ref.setBody("N");
longitude_ref.setHeader(QNetworkRequest::ContentDispositionHeader,QVariant("form-data; name=\"longitude_ref\""));
longitude_ref.setBody("E");
multiPart->append(aluno);
multiPart->append(palavra_chave);
multiPart->append(latitude);
multiPart->append(latitude_ref);
multiPart->append(longitude);
multiPart->append(longitude_ref);
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; filename=\"image.png\"; name=\"file\""));
QFile *file = new QFile("/path/image.png");
file->open(QIODevice::ReadOnly);
imagePart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(imagePart);
QString sendPath = "http://localhost/api/images/";
QUrl url(sendPath);
QNetworkRequest request(url);
QString concatenated = "username:password";
QByteArray data = "Basic ";
data.append(concatenated.toLocal8Bit().toBase64());
request.setRawHeader("Authorization", data);
manager->post(request,multiPart);
}
void MyNetwork::replyfinished(QNetworkReply* reply)
{
qDebug() << "C++ - replyUpdateJsonFinished";
qDebug() << QString(reply->readAll());
qDebug() << QString(reply->errorString());
}
Because of the nature of multipart/form-data, each field that is sent must be sent in a different part. This is because each part in the request represents a different field, and is identified by the field name and contains the body that should be sent (in your case, mostly text).
You may have better luck creating a helper method for the text field that takes the field name and the content of it. That method would then be able to create a new part with the correct header (containing the field name) and the body (containing the data to send), without requiring you to repeat the same lines.

Resources