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
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.