Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QML: how to upload a photo from Camera to a web server using XMLHttpRequest



  • Hi,

    I'm using Qt 5.3 to build my first QML app.
    I have a Qt Quick application which takes a photo using Camera, from the QtMultimedia module.
    I would like to upload this image on a web server using XMLHttpRequest, through a POST request. The API I have to use requires multi-part mime header.
    How can I do that? Is it possible to build this request using QML only or do I need a bit of C++?

    My image is stored in an Image type (I can display it, it works) :
    @Image {
    id: photo
    }@

    My upload function looks like this, but is incomplete at the moment:
    @function uploadImage(server, callback) {
    var req = new XMLHttpRequest();
    req.onreadystatechange = function() {
    if (req.readyState == XMLHttpRequest.HEADERS_RECEIVED) {
    console.log("Debug: header: " + req.getAllResponseHeaders ());
    } else if (req.readyState == XMLHttpRequest.DONE) {
    console.log("Debug: xml response: " + req.responseText);
    }
    }

        req.open("POST", server + "/api/cAddImage");
        // here I don't know to to build the request to send 'photo'???
        req.setRequestHeader(???);
        req.send(???);
    }@
    

    Thanks in advance,
    Best regards


  • Lifetime Qt Champion



  • Thank you for your answer.

    I found this post before and it works to send text data.
    E.g. , here is my code to send credentials to log on the server:

    @req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    req.send("username=" + username + "&password=" + password);@

    But I cannot find the right syntax to send an image???


  • Moderators

    May be using FormData. Never tried though.



  • Here is my solution: image uploading is performed by a separate C++ class, using QNetworkAccessManager:

    @#ifndef IMAGEUPLOADER_H
    #define IMAGEUPLOADER_H

    #include <QDebug>
    #include <QFile>
    #include <QFileInfo>
    #include <QHttpMultiPart>
    #include <QNetworkAccessManager>
    #include <QNetworkReply>
    #include <QNetworkRequest>
    #include <QString>

    class ImageUploader: public QObject {
    Q_OBJECT

    public:
    ImageUploader(QObject* parent = 0)
    : QObject (parent)
    , m_networkAccessManager(NULL)
    , m_networkReply (NULL) {
    m_networkAccessManager = new QNetworkAccessManager(this);
    }

    public slots:
    void uploadImage(const QString& imageFilename) {
    QFileInfo fileInfo(imageFilename);
    QFile* file = new QFile(imageFilename);
    if (!file->open(QIODevice::ReadWrite)) {
    emit imageUploaded(999, "Image not found");
    return;
    }

        emit imageUploaded(-1, "Try to upload image to server, please wait...");
    
        QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
        QHttpPart imagePart;
        imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
        imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(QString("form-data; name=\"image\"; filename=\"%1\"").arg(fileInfo.fileName()).toLatin1()));
        imagePart.setBodyDevice(file);
        file->setParent(multiPart);
        multiPart->append(imagePart);
    
        QNetworkRequest request("http://www.myserver.com/uploadImage");
        m_networkReply = m_networkAccessManager->post(request, multiPart);
        multiPart->setParent(m_networkReply);
    
        connect(m_networkReply, SIGNAL(finished()), this, SLOT(uploadImageFinished()));
    

    private slots:
    void uploadImageFinished() {
    QString xmlReply = QString(m_networkReply->readAll());
    delete m_networkReply;
    qDebug() << xmlReply;
    // here, you can parse the reply from the server...
    emit imageUploaded(0, "Image successfully uploaded");
    }

    signals:
    // errorCode = -1 : start image uploading
    // 0 : success
    // >0: error
    void imageUploaded(int errorCode, const QString& errorMessage);

    private:
    QNetworkAccessManager* m_networkAccessManager;
    QNetworkReply* m_networkReply;
    };

    #endif // IMAGEUPLOADER_H
    @

    To access the "image uploader" object from QML, we need to perform the binding, for example, in the main...

    @#include <QApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include "ImageUploader.h"

    int main(int argc, char* argv[])
    {
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    ImageUploader imageUploader;
    engine.rootContext()->setContextProperty("imageUploader", &imageUploader);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
    return app.exec();
    

    }
    @

    Finally, uploadImage() method is directly used in the QML code which can also retrieve signal emitted by the C++ code. Something like that...

    @import QtQuick 2.0
    import QtMultimedia 5.3

    Rectangle {
    id: pageCapture

    Connections { // to receive "imageUploaded" signal from the C++ code
        target: imageUploader 
    
        onImageUploaded: {
            console.log("onImageUploaded: errorCode=" + errorCode + " errorMessage=" + errorMessage);
        }
    }
    
    Camera {
        id: camera 
    
        imageCapture {
            onImageSaved: {
                // call the C++ method to upload image (filename is 'path')
                imageUploader.uploadImage(path);
            }
        }
    }
    
    VideoOutput {
        id: videoOutput
        source: camera
        anchors.fill: parent
        focus : visible
        fillMode: VideoOutput.PreserveAspectCrop
    }    
    

    }
    @



  • I can not get the code to work. Maybe the code of uploadimage.cpp is missing? Anybody can help me? Thanks in advance



  • I can not get the code to work. Maybe the code of uploadimage.cpp is missing? Anybody can help me? Thanks in advance


Log in to reply