Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QNetworkAccessManager: post http multipart from serial QIODevice

I'm trying to use QNetworkAccessManager to upload http multiparts to a dedicated server.

The multipart consists of a JSON part describing the data being uploaded.

The data is read from a serial QIODevice, which encrypts the data.

This is the code that creates the multipart request:

QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

QHttpPart metaPart;
metaPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
metaPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"metadata\""));
metaPart.setBody(meta.toJson());
multiPart->append(metaPart);

QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(fileFormat));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\""));
filePart.setBodyDevice(p_encDevice);
p_encDevice->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(filePart);

QNetworkAccessManager netMgr;
QScopedPointer<QNetworkReply> reply( netMgr.post(request, multiPart) );
multiPart->setParent(reply.data()); // delete the multiPart with the reply

If the p_encDevice is an instance of QFile, that file gets uploaded just fine.

If the specialised encrypting QIODevice is used (serial device) then all of the data is read from my custom device. however QNetworkAccessManager::post() doesn't complete (hangs).

I read in the documentation of QHttpPart that:

if device is sequential (e.g. sockets, but not files), QNetworkAccessManager::post() should be called after device has emitted finished().

Unfortunately I don't know how do that.

Please advise.

EDIT:

QIODevice doesn't have finished() slot at all. What's more, reading from my custom IODevice doesn't happen at all if QNetworkAccessManager::post() is not called and therefore the device wouldn't be able to emit such an event. (Catch 22?)

EDIT 2:

It seems that QNAM does not work with sequential devices at all. See discussion on qt-project.

EDIT 3:

I managed to "fool" QNAM to make it think that it is reading from non-sequential devices, but seek and reset functions prevent seeking. This will work until QNAM will actually try to seek.

bool AesDevice::isSequential() const
{
    return false;
}

bool AesDevice::reset()
{
    if (this->pos() != 0) {
        return false;
    }
    return QIODevice::reset();
}

bool AesDevice::seek(qint64 pos)
{
    if (this->pos() != pos) {
        return false;
    }
    return QIODevice::seek(pos);
}
like image 505
matejk Avatar asked Feb 27 '13 10:02

matejk


1 Answers

You'll need to refactor your code quite a lot so that the variables you pass to post are available outside that function you've posted, then you'll need a new slot defined with the code for doing the post inside the implementation. Lastly you need to do connect(p_encDevice, SIGNAL(finished()), this, SLOT(yourSlot()) to glue it all together.

You're mostly there, you just need to refactor it out and add a new slot you can tie to the QIODevice::finished() signal.

like image 56
Nicholas Smith Avatar answered Sep 30 '22 05:09

Nicholas Smith