C++ Qt QFileSystemWatcher file upload double



  • I am having issues where the file is being uploaded twice to the server.

    I am using the QFileSystemWatcher class from C++ Qt on Windows XP to send a file when the folder changes The files are small (1-12kb).

    The application sends the files by scanning the folder whenever it changes (on directoryChanged signal), loop through the files and send the one that I need. The server responds with an xml file that is returned into the same folder for another application to processes it.

    Apparently what’s happening is that on some systems there are 2 very fast directoryChanged signals at nearly the same time and there are two very fast file uploads happening.

    The server is running Apache and PHP, and there’s a simple MUTEX in place on the PHP side, but I just wanted to get to the root of the problem which seems to be on the Qt side. I'm open to using another class, another library or straight C++.

    Here is some code, I stripped all the irrelevant content:
    @this->w = new QFileSystemWatcher();
    this->w->addPath("C:/POSERA/MaitreD/DATA/INT");

    QStringList directoryList = w->directories();
    Q_FOREACH(QString directory, directoryList)
    qDebug() << "Watching Main Directory name: " << directory << endl;

    DirectoryWatcher* dw = new DirectoryWatcher;

    QObject::connect(
    this->w, SIGNAL(directoryChanged(const QString&)),
    dw, SLOT(directoryChanged(const QString&))
    );@



  • and the DirectoryWatcher.cpp:
    @DirectoryWatcher::DirectoryWatcher(QWidget* parent) : QWidget(parent)
    {
    lockSend = false;
    }

    void DirectoryWatcher::directoryChanged(const QString& str) {

    directoryLastChanged = str;
    
    QByteArray byteArray = str.toUtf8();
    const char* cString = byteArray.constData();
    
    sendChangedFiles(cString);
    

    }

    void DirectoryWatcher::sendChangedFiles(const char* path) {

    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir (path)) != NULL) {
    
        QString str;
    
        while ((ent = readdir (dir)) != NULL) {
            str = QString("%1/%2").arg(path, ent->d_name);
    
            QFileInfo info(str);
    
    
    
            if (lockSend == false && (info.completeSuffix() == "xml" || info.completeSuffix() == "XML") && info.baseName() != "" && !info.baseName().startsWith("REDM") && !info.baseName().startsWith("REFT")) {
    
                // reset the counter.
                this->resendCounter = 0;
    
                sendFileAndAccept(str.toUtf8().constData());
    
            }
    
        }
    
        closedir (dir);
    } else {
        qDebug() << "Could not open directory" << endl;
    }
    

    }

    class QNetworkRequest;
    class QNetworkReply;
    void DirectoryWatcher::sendFileAndAccept(const char* path) {

    // increment the resend counter
    this->resendCounter++;
    
    QFileInfo fileInfo(path);
    
    QNetworkAccessManager * mgr = new QNetworkAccessManager(this);
    connect(mgr,SIGNAL(finished(QNetworkReply*)),this,SLOT(saveResponse(QNetworkReply*)));
    connect(mgr,SIGNAL(finished(QNetworkReply*)),mgr,SLOT(deleteLater())); // @todo delete later
    
    QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
    
    QHttpPart filePart;
    filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/xml")); // @todo test
    filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"someFile\"; filename=\"" + fileInfo.baseName() + ".xml\""));
    
    currentFileSent = fileInfo.baseName();
    
    QFile *file = new QFile&#40;path&#41;;
    file->open(QIODevice::ReadOnly);
    filePart.setBodyDevice(file);
    file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
    
    multiPart->append(filePart);
    
    // POST request
    QNetworkReply *reply = mgr->post(QNetworkRequest(QUrl(XXXXXX)), multiPart);
    
    multiPart->setParent(reply); // delete the multiPart with the reply
    
    // lock
    lockSend = true;
    

    }

    void DirectoryWatcher::saveResponse(QNetworkReply *rep) {

    // get the response
    QByteArray bts = rep->readAll();
    QString str(bts);
    
    // compute new path
    QString partName = currentFileSent.mid(1, currentFileSent.length());
    QString newPath = QString("%1/A%2.xml").arg(directoryLastChanged, partName);
    
    qDebug() << "new path: " << newPath << endl;
    
    switch (rep->error()) {
        case QNetworkReply::NoError: {
            qDebug() << "NO ERROR" << endl;
    
            // save response to a file.
    
            QFile file&#40;newPath&#41;;
            file.open(QIODevice::WriteOnly | QIODevice::Text);
            QTextStream out(&file);
            out << str;
    
            file.close();
    
            break;
        }
        default:
    

    // case QNetworkReply::TimeoutError :
    // case QNetworkReply::HostNotFoundError :
    qDebug() << "NETWORK REPLY ERROR" << endl;
    // resend the file if the counter is < 10
    if (this->resendCounter < 5) {

                // delay by n sec
                QTime dieTime = QTime::currentTime().addSecs(1);
                while( QTime::currentTime() < dieTime )
                    QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
    
                sendFileAndAccept(this->lastPathSent.toStdString().c_str());
            } else {
    
                // after 10 attempts, we're probably sure that the network is down
                // save the file somewhere and generate a default one to prevent timeouts.
    
                qDebug() << "Saving file for later..." << endl;
                if (!saveFileForLater(lastPathSent.toStdString().c_str())) {
                    qDebug() << "ERROR SAVING FILE, CHECK IF FOLDER EXISTS AND THE PERMISSIONS." << endl;
                }
    
                // generate a default one to prevent timeouts.
                qDebug() << "Generate a default file..." << endl;
                // ...
            }
    
            break;
    }
    
    // unlock
    lockSend = false;
    
    rep->deleteLater(); // prevent memory leak
    

    }

    bool DirectoryWatcher::saveFileForLater(const char* pathToRequestFile) {

    QFile file&#40;pathToRequestFile&#41;;
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "readonly and text" << endl;
        return false;
    }
    
    QString path(pathToRequestFile);
    QFileInfo fileinfo(path);
    
    QString newPath = "C:\\data\\offline\\" + fileinfo.fileName();
    
    return file.copy(newPath);
    

    }@


Log in to reply
 

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