2013-08-22 63 views
2

我遇到了文件上傳兩次到服務器的問題。C++ Qt QFileSystemWatcher文件上傳雙重

我使用Windows XP上的C++ Qt的QFileSystemWatcher類在文件夾更改時發送文件文件很小(1-12kb)。

應用程序通過掃描文件夾(在directoryChanged信號上)時發送文件,循環遍歷文件併發送我需要的文件。服務器響應一個xml文件,該文件返回到另一個應用程序的相同文件夾中進行處理。

顯然發生了什麼事情是,在某些系統上幾乎同時有兩個非常快速的directoryChanged信號,並且有兩個非常快速的文件上傳發生。

服務器正在運行Apache和PHP,並且PHP端有一個簡單的MUTEX,但我只想找到似乎在Qt端的問題的根源。我打算使用另一個類,另一個庫或直C++。

下面是一些代碼,我全部剝離不相關的內容:

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&))); 

和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); 

} 

感謝您的幫助。

+0

我懷疑你會找到一個解決方案。我可以證實這種行爲。雖然我擁有大文件(> 500MB)。在複製過程中,文件系統監視器會多次啓動。我也必須實施某種'互斥'。但我仍然想知道,你可以用像你這樣的小文件來看它。 – Greenflow

+0

我不明白這個問題。兩個文件只需要一個directoryChanged信號(在已知時鐘上實現節流)。或者你想分別處理每個文件? – rileyberton

+0

感謝您的答案傢伙。 @Greenflow我認爲是相反的,小文件在文件上傳場景中更容易出錯。 –

回答

2

2發出directoryChanged的最可能原因是保存更改時的普通編輯器刪除並將新版本的文件寫入磁盤。這就是爲什麼當文件被刪除時有一個信號,而當它被重新創建時有一個信號。

+0

是的,就是這樣。謝謝。 –