Home > Mobile >  Multiple get/post calls using a wrapper class with Qt
Multiple get/post calls using a wrapper class with Qt

Time:11-17

I am working on a Qt project with a team. I have two functions — one retrives the numerical coordinates of a place, the other downloads the map of the place — that I want to merge in one wrapper class, so that my teammates can call it easily.

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

class OpenStreetMapWrapper: public QObject{
    Q_OBJECT
public:
    OpenStreetMapWrapper(QObject *parent=nullptr):QObject(parent){
        connect(&manager, &QNetworkAccessManager::finished, this, &OpenStreetMapWrapper::handle_finished);
    }

    void download(const std::string &region, const std::string &department, const QFile& outfile){
        QNetworkRequest request;
        QUrl url = QUrl(QString::fromStdString("https://download.openstreetmap.fr/extracts/europe/france/"   region   "/"   department   ".osm.pbf"));
        request.setUrl(url);
        request.setAttribute(QNetworkRequest::User, outfile.fileName());
        manager.get(request);
    }

    void searchCSV(QFile& file, QFile& outfile){
        QNetworkRequest request(QUrl("https://api-adresse.data.gouv.fr/search/csv/")); // Free API provided by the French government
        request.setAttribute(QNetworkRequest::User, outfile.fileName());
        QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
        QHttpPart postpart;
        postpart.setHeader(QNetworkRequest::ContentDispositionHeader,
                             QString("form-data; name=%1; filename=%2")
                             .arg("data", file.fileName()));
        postpart.setBodyDevice(&file);
        multipart->append(postpart);
        file.setParent(multipart);
        manager.post(request, multipart);
    }

private:
    QNetworkAccessManager manager;
    void handle_finished(QNetworkReply *reply){
        if(reply->error() == QNetworkReply::NoError){
            QByteArray read = reply->readAll();
            std::cout << read.toStdString() << std::endl; // For debugging
            QString filename = reply->request().attribute(QNetworkRequest::User).toString();
            QFile out(filename);
            if(out.open(QIODevice::WriteOnly)){
                out.write(read);
                out.close();
            }
        }
        else{
            qDebug() << reply->error() << reply->errorString();
        }
        reply->deleteLater();
        // QCoreApplication::quit(); This is done somewhere else?
    }
};
#include <main.moc>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    OpenStreetMapWrapper A;

    QFile file("./search.csv");
    file.open(QIODevice::ReadWrite);

    QFile outfile("./output.csv");
    outfile.open(QIODevice::ReadWrite);

    // Search

    A.searchCSV(file, outfile); // 1st call works
    A.searchCSV(file, outfile); // 2nd call -> makes both calls fail.

    // Downloader

    std::string region = "corse";
    std::string department = "haute_corse";

    return a.exec();
}

The problem with the code above, is that when for example searchCSV is called it displays the output as needed, but if it is called twice in the code, there is no output at all. After some debugging I think the problem is that the manager and handle_finished are not connected properly, because the execution never reaches there. Is there a simple way to solve this issue? Ideally, there is just one class instant, and any method can be called any number of times.

CodePudding user response:

I don't know much about Qt, but it looks like you're trying to read from file twice and my guess is that when it reaches the end of the file after the first call to A.searchCSV it is done and you can't read from it anymore - unless you reposition the QFile to the beginning of the file.

Possible solution:

    A.searchCSV(file, outfile);
    file.unsetError();           // possibly needed
    file.seek(0);                // rewind to the start of the file
    A.searchCSV(file, outfile);

CodePudding user response:

The two QFile (input, output) are shared between two asynchronous calls (searchCSV) that might give an undefined behavior. The input file (stream) contents will be load and push only after the connection was made (like curl does).

You should:

  • Make searchCSV a blocking function (wait until handle_finished() done), input file pointer should be reinitialized before an other call.
  • OR: Use separated input/output QFile instances
  • Related