【问题标题】:C++ Qt QFileSystemWatcher file upload doubleC++ Qt QFileSystemWatcher 文件上传双
【发布时间】:2013-08-22 22:32:54
【问题描述】:

我遇到了文件被两次上传到服务器的问题。

我在 Windows XP 上使用 C++ Qt 中的 QFileSystemWatcher 类在文件夹更改时发送文件文件很小 (1-12kb)。

应用程序通过在文件夹更改时(在 directoryChanged 信号上)扫描文件夹来发送文件,循环浏览文件并发送我需要的文件。服务器响应一个 xml 文件,该文件返回到同一文件夹中,供另一个应用程序处理。

显然,在某些系统上,几乎同时有 2 个非常快的 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);

}

感谢您的帮助。

【问题讨论】:

  • 我怀疑你会找到解决方案。我可以确认这种行为。虽然我有大文件(> 500mb)。 filesystemwatcher 在复制过程中触发了几次。我还必须实现某种“互斥锁”。但我仍然有点想知道,你可以用像你这样的小文件看到它。
  • 我不明白这个问题。您是否只需要两个文件的 1 个 directoryChanged 信号(在已知时钟上实施节流阀)。还是要单独处理每个文件?
  • 谢谢你们的回答。 @Greenflow 我认为是相反的,小文件在文件上传场景中更容易出错。
  • @rileyberton 在我描述的情况下,只有一个文件要发送,但应用程序发送同一个文件 2 次。 PHP 将它们解释为 2 个独立的线程,这些线程有时可能会进入相同的临界区(没有实现互斥锁)。我想要的是发送一个文件,然后等待响应(lockSend 应该对此负责)。我确信这是可能的,如果像 dropbox 这样的应用程序没有问题,那么我认为我不应该用某种 DIY 解决方案重新发明轮子,我有点失望像 Qt 这样的框架并不能阻止这一点从发生
  • 因此您可以保留一个短暂的集合来跟踪飞行中的文件,并且不允许基于名称的重复。上传完成后,只需从集中删除文件即可。这可以防止 sendChangedFiles 周围的互斥锁。

标签: c++ qt qfilesystemwatcher


【解决方案1】:

发出 2 次 directoryChanged 的​​最可能原因是普通编辑器在保存更改时会删除文件的新版本并将其写入磁盘。这就是为什么文件被删除时有一个信号,而重新创建时有一个信号的原因。

【讨论】:

  • 是的,就是这样。谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-12-14
  • 1970-01-01
  • 2016-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多