Solved Send image inside Json through QTcpSocket
-
Hello everyone.
I have a local client-server application where data in sent through
QTcpSockets
. At a certain point, the client asks for a photo, and the server must send it to the client.Server
Setup:
QByteArray info=""; // Data to be send QJsonObject qjo; QDataStream ds(socket);
The server loads the image from the filesystem and fills the related
QByteArray ba
:QImage imageObject; imageObject.load(Path/to/image); QPixmap image = QPixmap::fromImage(imageObject); QByteArray ba; // Construct a QByteArray object QBuffer buffer(&ba); // Construct a QBuffer object using the QbyteArray image.save(&buffer, "JPG"); // Save the QImage data into the QBuffer
For my application, the server should send not only the image, but also a code meaning that this is the "case" of a photo request:
qjo.insert("request_type", PHOTO); /* PHOTO (=13) is defined in a .h */
Now, I would like to do the same thing with
ba
:qjo.insert("photo_data", [some way to put ba]); // I can't put ba directly...
because the final step is this one:
QJsonDocument doc(qjo); QString strJson(doc.toJson(QJsonDocument::Compact)); info.append(strJson); ds << info;
I've tried this:
qjo.insert("photo_data", ba.data()); /* ba.data() = "\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD" */
Client
It reads all the data correctly. Then, when it extractsphoto_data
, this doesn't seem to work:QJsonValue photo = obj.take("photo"); /* photo = QJsonValue(string, "????") */
So, how can I send the
QByteArray
from server to client properly? -
e.g. by converting the QByteArray to base64 encoding.
-
@Christian-Ehrlicher
Hi, i tried:qjo.insert("photo", ba.toBase64());
but the IDE says:
no viable conversion from 'QByteArray' to 'const QJsonValue'
-
@Tamfub said in Send image inside Json through QTcpSocket:
but the IDE says:
The compiler maybe, but not the IDE...
You have to convert the QByteArray to a QString.
-
@Christian-Ehrlicher
Yes sorry, I meant the compiler. By the way, I tried bothqjo.insert("photo", QString::fromStdString(ba.toBase64().toStdString()));
and
qjo.insert("photo", QString::fromUtf8(data));
but the result in the client is:
QJsonValue photo = obj.take("photo"); qDebug() << photo.toString(); // Output: "????"
And data sent by the server for
photo
is"\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD\xEF\xBF\xBD"
again. -
@Tamfub said in Send image inside Json through QTcpSocket:
qjo.insert("photo", QString::fromStdString(ba.toBase64().toStdString()));
Have you tried this
qjo.insert("photo", QString(ba.toBase64()));
?And then:
QJsonValue photo = obj.take("photo"); qDebug() << photo.toString();
-
Then you do something wrong:
QJsonObject obj; obj["photo"] = QString::fromLatin1(QByteArray("\x01\x02").toBase64()); qDebug() << obj["photo"].toString(); // outputs the base64 string 'AQI='
QByteArray::toHex() can also be used but is larger.
-
@KroMignon
Yes, I tried and this is the result:Server
qjo.insert("photo", QString(ba.toBase64())); qDebug() << QString(ba.toBase64)); // No output...
Client
QJsonValue photo = obj.take("photo"); qDebug() << photo.toString(); // No output...
-
@Tamfub said in Send image inside Json through QTcpSocket:
qjo.insert("photo", QString(ba.toBase64()));
Sorry, my fault ==>
qjo.insert("photo", QString::fromLatin1(ba.toBase64()));
(as @Christian-Ehrlicher already wrote) -
@Christian-Ehrlicher
I tried these:Server
QJsonObject obj; obj["photo"] = QString::fromLatin1(ba.toBase64()); qjo.insert("photo", obj["photo"].toString()); qDebug() << obj["photo"].toString(); // "AQI="
Client
QJsonValue photo = obj.take("photo"); qDebug() << photo.toString(); // "AQI="
/********************/
Server
qjo.insert("photo", QString::fromLatin1(ba.toHex())); qDebug() << QString::fromLatin1(ba.toHex()); // No output...
Client
QJsonValue photo = obj.take("photo"); qDebug() << photo.toString(); // No output...
/********************/
Server
qjo.insert("photo", QString::fromLatin1(ba.toBase64()); qDebug() << QString::fromLatin1(ba.toBase64()); // No output...
Client
QJsonValue photo = obj.take("photo"); qDebug() << photo.toString(); // No output...
-
Because you unpack your json structure wrong I would guess. In the first example you pack it twice as you can see.
-
@Christian-Ehrlicher
Ok, I removed theqjo.insert("photo", obj["photo"].toString());
line but it still won't work.By the way, I noticed something in my client. This is the part where it receives data from the server:
QByteArray dataReceived; QDataStream ds(sock); while(sock->bytesAvailable()){ ds.startTransaction(); ds >> dataReceived; } ds.commitTransaction(); while(sock->bytesAvailable()){ // Start the transaction ds.startTransaction(); // Read data ds >> dataReceived; // Error checking if ((!ds.commitTransaction()) && (ds.status()!=QDataStream::Ok)){ sock->flush(); qDebug() << "Error!"; return; } } QString string = (QString)dataReceived; QJsonDocument doc = QJsonDocument::fromJson(string.toUtf8()); QJsonObject obj = doc.object(); ... QJsonValue photo = obj.take("photo"); qDebug() << photo.toString();
Then the output, in all the examples, is:
Error! Error! Error! Error! Error! Error!
and it reaches the part where it reads the photo data. There is something wrong when the client receives data, maybe?
-
Sorry but you should start with one problem first. Properly pack/unpack the data in your json string. Then we can go over to the next problem.
-
@Tamfub
Dunno, let's start withwhile(sock->bytesAvailable()){ ds.startTransaction(); ds >> dataReceived; } ds.commitTransaction();
Why would you (at least potentially) start multiple transactions and only commit once at the end?
Next, I don't understand your whole sequential
bytesAvailable()
loops. Why two loops? Are you sure your transactions/tests tally against what is being sent in the same fashion?Practice code just sending string
"hello"
, perhaps with transactions commented out, while you verify the protocol is correct? Then move to the base64 stuff. And debug out the first & last few characters sent & received to verify they correspond before you look at decoding. You want to discover where your issue is little by little. -
@Christian-Ehrlicher @JonB
Ok, I guess I'll have to revise my protocol, then I'll return to the other problem later :) -
Hi,
@Tamfub said in Send image inside Json through QTcpSocket:
QString string = (QString)dataReceived;
dataReceived is a QByteArray, that cast is completely wrong.
-
@Christian-Ehrlicher @JonB
I've given a look to the fortune client and server examples and tried to toy with data.In
server.cpp
, I replacedout << fortunes[QRandomGenerator::global()->bounded(fortunes.size())];
with
QString str; for(int i=0; i<1000000; i++) str.append("a"); out << str;
void Client::readFortune() became:
void Client::readFortune() { in.startTransaction(); QString data; in >> data; qDebug() << "data: " << data; if (!in.commitTransaction()) return; qDebug() << "data (end): "; qDebug() << data; // if (nextFortune == currentFortune) { // QTimer::singleShot(0, this, &Client::requestNewFortune); // return; // } // currentFortune = nextFortune; // //statusLabel->setText(currentFortune); // qDebug() << "current: " << currentFortune; // getFortuneButton->setEnabled(true); qDebug() << "finished"; }
The output in the client is:
next: "" next (end): finished
I guess
qDebug()
does not print large data? -
@Tamfub said in Send image inside Json through QTcpSocket:
I guess qDebug() does not print large data?
Do not rely on
qDebug()
output visibility. I think it says somewhereqDebug(qPrintable(qString))
; or printQString::length()
. -
@SGaist
Hi.
Ok, I guess I should use something likeQString::fromLatin1(array)
, right? -
@JonB
Ok, I put some printing of the sizes, and found out that receivedData.size() = 2 x sentData.size().Server
ds << QString::fromLatin1(ba.toBase64()); qDebug() << "size: " << QString::fromLatin1(ba.toBase64()).size(); // 75684
Client
ds.startTransaction(); QByteArray data; ds >> data; if (!ds.commitTransaction()) return; qDebug() << "data(size): " << risp.size(); // 151368
How is this possible? If I try with
QByteArray str; for(int i=0; i<1000000; i++) str.append("a"); ds << str; qDebug() << "size: " << str.size(); // 1000000
instead, the two sizes match!