0

So I'm trying to upload a simple text file using Qt Network Mangager to a php script that I'm serving. But it's not working. I tried examples with QHttpMultiPart and with Setting raw data headers in request but none work.

Here is my Qt Code:

#include <QCoreApplication>

#include <QNetworkReply>
#include <QNetworkRequest>
#include <QDebug>
#include <QEventLoop>
#include <QObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <QFile>
#include <QHttpMultiPart>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString address = "http://localhost/api_test/";
    //address = "https://dashboard.viewmind.ai/dashboard/api_test/welcome.php";

    QUrl api_url = QUrl(address);

    QVariantMap postDatamap;
    postDatamap.insert("Name","Ariel Ñoño");
    postDatamap.insert("Age",37);

    QJsonDocument json = QJsonDocument::fromVariant(postDatamap);

    qDebug() << "Sending the request";
    QNetworkAccessManager *networkManager = new QNetworkAccessManager();
    QNetworkRequest request(api_url);


    QString bound = "<<<<<boundary>>>>>";
    request.setRawHeader(QString("Content-Type").toUtf8(),QString("multipart/form-postData; boundary=" + bound).toUtf8());


    //QByteArray postData;
    QByteArray postData(QString("--" + bound + "\r\n").toUtf8());
    postData.append("Content-Disposition: form-postData; name=\"action\"\r\n\r\n");
    postData.append("welcome.php\r\n");
    postData.append(QString("--" + bound + "\r\n").toUtf8());
    postData.append("Content-Disposition: form-postData; name=\"uploaded\"; filename=\"");
    postData.append("test.json");
    postData.append("\"\r\n");
    postData.append("Content-Type: text/xml\r\n\r\n"); //postData type

    QFile file("test.json");
        if (!file.open(QIODevice::ReadOnly)){
            qDebug() << "QFile Error: File not found!";
            delete networkManager;
            return 0;
        } else { qDebug() << "File found, proceed as planned"; }

    postData.append(file.readAll());
    postData.append("\r\n");
    postData.append(QString("--" + bound + "\r\n").toUtf8());

    request.setRawHeader(QString("Content-Length").toUtf8(), QString::number(postData.length()).toUtf8());


    //request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json; charset=utf-8");
    //request.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-postData; name=\"text\""));
    //qDebug() << QString(json.toJson());

    //QHttpMultiPart multipart(QHttpMultiPart::FormDataType);
    //QHttpPart textPart;

//    QFile file("test.json");
//    if (!file.open(QIODevice::ReadOnly)){
//        qDebug() << "Could not open file for reading";
//        delete networkManager;
//        return 0;
//    }
    //textPart.setBodyDevice(&file);
    //multipart.append(textPart);
    //file.setParent(&multipart);

    //QNetworkReply *reply = networkManager->post(request,json.toJson());
    //QNetworkReply *reply = networkManager->post(request,file.readAll());
    //QNetworkReply *reply = networkManager->post(request,&multipart);
    QNetworkReply *reply = networkManager->post(request,postData);
    //file.setParent(reply);
    //multipart.setParent(reply);

    // Using the loop to wait for the reply to finish.
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    qDebug() << "Reply is finished";

    //file.close();

    if (reply->error() != QNetworkReply::NoError){
        qDebug() << "The following error ocurred";
        qDebug() << reply->errorString();
        return 0;
    }

    QString postData_returned = QString::fromUtf8(reply->readAll());

    qDebug() << "DATA RETURNED";
    qDebug() << postData_returned;

    return 0;
}

My php code looks like this

<?php
header("Access-Control-Allow-Origin: *");  // Anyone can access
header("Content-Type: application/json; charset=UTF-8"); // Will return json data. 

error_log("FILES iS");
vardump_toerror($_FILES);

?>

It is my understanding that the $_FILES super global should get filled with the file information. Am I mistaken? But the print out shows it's empty.

aarelovich
  • 5,140
  • 11
  • 55
  • 106

1 Answers1

0

I am not an expert in PHP but I find it unnecessary to use the content-type application/json since multipart (submit forms) is not part of that protocol. On the other hand I can't find a reference of the vardump_toerror function so I change with var_dump so my test php is:

<?php
  var_dump($_FILES);
?>

In a previous question for PyQt5 I implemented a similar logic for django that also applies in this case so I will show a translation.

#include <QCoreApplication>
#include <QFile>
#include <QHttpMultiPart>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTextCodec>

QHttpMultiPart *buildMultpart(const QVariantMap & data, const QMap<QString, QString> filenames){

    QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
    QVariantMap::const_iterator i_data = data.constBegin();
    while (i_data != data.constEnd()) {
        QHttpPart postpart;
        postpart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(i_data.key()));
        postpart.setBody(i_data.value().toByteArray());
        multipart->append(postpart);
        ++i_data;
    }
    QMap<QString, QString>::const_iterator i_filenames = filenames.constBegin();
    while (i_filenames != filenames.constEnd()) {

        QFile *file = new QFile(i_filenames.value());
        if(!file->open(QIODevice::ReadOnly)){
            delete  file;
            continue;
        }
        QHttpPart postpart;
        postpart.setHeader(QNetworkRequest::ContentDispositionHeader,
                           QString("form-data; name=\"%1\"; filename=\"%2\"")
                           .arg(i_filenames.key(), file->fileName()));
        postpart.setBodyDevice(file);
        multipart->append(postpart);
        file->setParent(multipart);
        ++i_filenames;
    }
    return multipart;
}


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QUrl url("http://localhost:4000/upload.php");
    QNetworkAccessManager manager;
    QMap<QString, QString> filenames;
    filenames["fileToUpload"] = "/path/of/data.txt";
    QHttpMultiPart *multipart = buildMultpart({}, filenames);
    QNetworkRequest request(url);
    QNetworkReply *reply = manager.post(request, multipart);
    multipart->setParent(reply);
    QObject::connect(reply, &QNetworkReply::finished, QCoreApplication::quit);
    a.exec();
    if(reply->error() == QNetworkReply::NoError){
        qDebug() << reply->readAll();
    }
    else{
        qDebug() << reply->error() << reply->errorString();
    }
    delete reply;

    return 0;
}

Output:

"array(1) {\n  [\"fileToUpload\"]=>\n  array(5) {\n    [\"name\"]=>\n    string(8) \"data.txt\"\n    [\"type\"]=>\n    string(0) \"\"\n    [\"tmp_name\"]=>\n    string(14) \"/tmp/phpVmOAhO\"\n    [\"error\"]=>\n    int(0)\n    [\"size\"]=>\n    int(6)\n  }\n}\n"
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • You are the MAN!!! Worked like a charm. Sorry about vardump_toerror. it was just a function that vardumps a variable to a string and the vardump to the error log. I used your code AS is and It worked. I'm gonna figure out what the difference with mine was. Thank you!!! – aarelovich May 16 '21 at 12:46