Search code examples
c++qtfilehttpqbuffer

Segmentation fault error when trying to upload data from a QBuffer


I have never coded with C++ nor with Qt, but I wanted to customize Flameshot to upload images to my self-hosted Picsur instance. However, Picsur uses a different method for uploading images via api than Imgur to which Flameshot uploads originally.

I did figure out how to make the http request and I have code that works completely, but my problem is, that in order to make it work I have to save the QPixmap to a file and then upload that file which works but is not really efficient.

What I tried before saving and then uploading the file is to just use the QBuffer directly as the body device for the http request, but when I executed that I would get this error in my console: 18344 Segmentation fault (core dumped). I wasn't able to find any other errors, including with setting the variable QT_LOGGING_RULES to "*.debug=true".

Here's my functioning code that works by saving the file:

QByteArray byteArray;
QBuffer buffer(&byteArray);
buffer.open(QIODevice::WriteOnly);
pixmap().save(&buffer, "PNG");
buffer.close();

QSaveFile saveFile("tmp.png");
saveFile.open(QIODevice::WriteOnly);
saveFile.write(byteArray);
saveFile.commit();

QFile *file = new QFile("tmp.png");
file->open(QIODevice::ReadOnly);

QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\"; filename=\"" + FileNameHandler().parsedPattern() + "\""));
filePart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(filePart);

QUrl url(QStringLiteral("https://example.org/api/image/upload"));
QNetworkRequest request(url);
request.setRawHeader("Authorization",
                        QStringLiteral("Api-Key %1")
                        .arg(ConfigHandler().uploadClientSecret())
                        .toUtf8());

m_NetworkAM->post(request, multiPart);

Here's the code that throws the segmentation error:

QByteArray byteArray;
QBuffer *buffer = new QBuffer(&byteArray);
buffer->open(QIODevice::WriteOnly);
pixmap().save(buffer, "PNG");
buffer->seek(0);

QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\"; filename=\"" + FileNameHandler().parsedPattern() + "\""));
filePart.setBodyDevice(buffer);
multiPart->append(filePart);

QUrl url(QStringLiteral("https://example.org/api/image/upload"));
QNetworkRequest request(url);
request.setRawHeader("Authorization",
                        QStringLiteral("Api-Key %1")
                        .arg(ConfigHandler().uploadClientSecret())
                        .toUtf8());

m_NetworkAM->post(request, multiPart);

The function pixmap() returns the QPixmap which holds the image data from the screenshot Flameshot took. m_NetworkAM is a variable from the class defined as such:

m_NetworkAM = new QNetworkAccessManager(this);
connect(m_NetworkAM,
        &QNetworkAccessManager::finished,
        this,
        &ImgurUploader::handleUpload);

For the sake of completeness, here's the original code from Flameshot:

QByteArray byteArray;
QBuffer buffer(&byteArray);
pixmap().save(&buffer, "PNG");

QUrlQuery urlQuery;
urlQuery.addQueryItem(QStringLiteral("title"), QStringLiteral(""));
QString description = FileNameHandler().parsedPattern();
urlQuery.addQueryItem(QStringLiteral("description"), description);

QUrl url(QStringLiteral("https://api.imgur.com/3/image"));
url.setQuery(urlQuery);
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,
                    "application/application/x-www-form-urlencoded");
request.setRawHeader("Authorization",
                        QStringLiteral("Client-ID %1")
                        .arg(ConfigHandler().uploadClientSecret())
                        .toUtf8());

m_NetworkAM->post(request, byteArray);

The whole file is viewable here: imguruploader.cpp

As I said, I'm completely new to C++ as well as Qt, so it could totally be possible that I made a completely obvious mistake here.


Solution

  • I solved this now using the QByteArray directly via the function setBody. Many thanks to @IgorTandetnik for pointing that out!

    This is the functioning code:

    QByteArray byteArray;
    QBuffer buffer(&byteArray);
    buffer.open(QIODevice::WriteOnly);
    pixmap().save(&buffer, "PNG");
    buffer.close();
    
    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
    QHttpPart filePart;
    filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\"; filename=\"" + FileNameHandler().parsedPattern() + "\""));
    filePart.setBody(byteArray);
    multiPart->append(filePart);
    
    QUrl url(QStringLiteral("https://example.org/api/image/upload"));
    QNetworkRequest request(url);
    request.setRawHeader("Authorization",
                            QStringLiteral("Api-Key %1")
                            .arg(ConfigHandler().uploadClientSecret())
                            .toUtf8());
    
    m_NetworkAM->post(request, multiPart);