C++ Qt QFileSystemWatcher file upload double
-
wrote on 23 Aug 2013, 04:19 last edited by
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&))
);@ -
wrote on 23 Aug 2013, 04:20 last edited by
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(path); 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(newPath); 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(pathToRequestFile); 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);
}@
1/2