【问题标题】:C++ Directory Watching - How to detect copy has endedC++ 目录监视 - 如何检测副本已结束
【发布时间】:2012-02-29 05:22:45
【问题描述】:

我有一个用于复制文件的文件夹。我想在将文件复制到目录后立即观看并处理文件。我可以检测文件何时在目录中,无论是通过轮询(我当前的实现)还是在使用我在网上找到的几个示例中的 Windows API 进行的一些测试中。

问题是我检测到文件何时首次创建并且仍在复制中。这使我需要访问文件的程序通过错误(因为文件尚未完成)。我怎样才能检测到复制开始的时间而不是复制结束的时间?我在 Windows 上使用 C++,所以答案可能取决于平台,但如果可能的话,我希望它与平台无关。

【问题讨论】:

  • 几分钟前我发现了这个问题:stackoverflow.com/questions/559659/… 这与我的问题非常相似,但“批准”的解决方案远非优雅和实用。
  • 如果您的代码将在管理上下文中运行,即作为系统服务,您可以使用更改日志。见msdn.microsoft.com/en-us/library/windows/desktop/…
  • 考虑到使用fread()/fwrite() 进行天真的复制等事情的可能性,我认为没有通用的解决方案。不太健壮,但也许通过跟踪文件的打开句柄?

标签: c++ windows visual-c++ directory


【解决方案1】:

您可以使用锁定文件或特殊命名约定。最简单的是后者,并且会像这样工作:

假设您要移动一个名为“fileA.txt”的文件,将其复制到目标目录时,请将其复制到“fileA.txt.partial”或类似的位置。复制完成后,将文件从“fileA.txt.partial”重命名为“fileA.txt”。所以“fileA.txt”的出现对于观看程序来说是原子的。

前面提到的另一个选项是锁定文件。因此,当您复制一个名为“fileA.txt”的文件时,首先要创建一个名为“fileA.txt.lock”的文件。复制完成后,您只需删除锁定文件。当观看程序看到“fileA.txt”时,它应该检查“fileA.txt.lock”是否存在,如果存在,它可以等待或将来根据需要重新访问该文件。

【讨论】:

  • 嗯,这是一种解决方法,可能适用于其他环境。但是在这种情况下,我说的是其他程序/通过文件资源管理器将文件复制到我的程序正在监视的文件夹中。在那种情况下,这种方法很难实现。
  • 如果您无法控制文件的复制方式,不确定是否可行。
【解决方案2】:

你不应该投票。使用 FindFirstChangeNotification (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364417%28v=vs.85%29.aspx) 监视目录的更改。

然后使用等待功能 (http://msdn.microsoft.com/en-us/library/windows/desktop/ms687069%28v=vs.85%29.aspx) 等待更改通知发生。

此处的概述和示例:http://msdn.microsoft.com/en-us/library/windows/desktop/aa365261%28v=vs.85%29.aspx

我不确定如何准确地确定文件写入完成。 Evan Teran 的回答是个好主意。

【讨论】:

  • 正是因为轮询不是一个好主意,我才试图用更好的东西代替它;)是的!我找到了这些方法,并且已经有了基于此的解决方案:code.google.com/p/simplefilewatcher。问题是确定文件写入完成。该库(我已经稍作修改)使用 ReadDirectoryChangesW,我相信它比 FindFirstChangeNotification 更强大。
  • 顺便说一句,我只是想知道 Dropbox(例如)如何处理此类问题。
  • 您可以尝试打开文件进行独占写入,直到复制完成才会失败。可能有一种方法可以阻止这种情况,而不是轮询它,但我不知道它是临时的。
  • 是的。我最终做了类似的事情,轮询时间很长(比如 3 或 5 秒)。它远非完美,但 Windows API 不允许有更好的东西。
【解决方案3】:

你可以使用这样的东西,这是经过测试和工作的

bool IsFileDownloadComplete(const std::wstring& dir, const std::wstring& fileName) 
{
    std::wstring originalFileName = dir + fileName;
    std::wstring tempFileName = dir + L"temp";

    while(true)
    {
        int ret = rename(convertWstringToString(originalFileName).c_str(), convertWstringToString(tempFileName).c_str());
        if(ret == 0)
            break;      

        Sleep(10);
    }   

    /** File is not open. Rename to original. */
    int ret = rename(convertWstringToString(tempFileName).c_str(), convertWstringToString(originalFileName).c_str());
    if(ret != 0)
        throw std::exception("File rename failed"); 

    return true;
}

【讨论】:

    猜你喜欢
    • 2016-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-14
    • 2013-08-24
    相关资源
    最近更新 更多