20

我正在写一个备份解决方案(各种)。只需从位置 C:\ 复制文件并将其粘贴到位置 Z:\

为确保速度快,在复制和粘贴之前检查原始文件是否存在。如果是这样,它会执行一些“计算”来确定副本是否应该继续或备份文件是否是最新的。我发现这些计算很困难。

最初,我比较了文件大小,但这还不够好,因为很有可能更改文件并且它的大小相同(例如,将字符 C 保存在记事本中的大小与我保存字符 T 的大小相同)。

所以,我需要找出修改日期是否不同。目前,我使用FileInfo该类获取文件信息,但在查看所有字段后,似乎没有任何合适的内容。

如何检查以确保我正在复制已修改的文件?

编辑 我已经看到关于使用 MD5 校验和的建议,但我担心这可能是一个问题,因为我正在比较的一些文件将高达 10GB

4

6 回答 6

23

修改日期是不可靠的 - 计算机时钟在同步或手动调整时可能会倒退。在管理修改日期方面,某些程序在修改或复制文件时可能表现不佳。

使用归档位可能在受控环境中工作,但如果另一个软件正在运行并使用归档位,会发生什么情况?

Windows 存档位是邪恶的,必须停止

如果你想要(几乎)完全的可靠性,那么你应该做的是使用像 SHA1 这样的良好散列函数存储最后备份版本的散列值,如果散列值发生变化,则上传新副本。

这是 SHA1 类以及底部的代码示例:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.sha1.aspx

只需通过它运行文件字节并存储哈希值。将 a 传递FileStream给它,而不是使用字节数组将文件加载到内存中,以减少内存使用量,尤其是对于大文件。

您可以将其与修改日期以各种方式结合起来,根据速度和可靠性的需要调整您的程序。例如,您可以检查大多数备份的修改日期,并定期运行在系统空闲时运行的哈希检查器,以确保没有遗漏任何内容。有时修改的日期会改变但文件内容仍然相同(即被相同的数据覆盖),在这种情况下,您可以避免在重新计算哈希并意识到它仍然相同后重新发送整个文件。

大多数版本控制系统使用某种结合哈希和修改日期的方法。

如果您不想进行完整备份并每次都发送所有数据,您的方法通常会涉及某种风险管理,并在性能和可靠性之间进行折衷。出于这个原因,偶尔进行一次“完整备份”很重要。

于 2012-10-22T15:36:22.207 回答
20

您可以通过哈希值比较文件:

private byte[] GetFileHash(string fileName)
{
    HashAlgorithm sha1 = HashAlgorithm.Create();
    using(FileStream stream = new FileStream(fileName,FileMode.Open,FileAccess.Read))
      return sha1.ComputeHash(stream);
}

如果内容发生了变化,哈希值会有所不同。

于 2012-10-22T15:41:02.177 回答
12

您可能想查看FileSystemWatcher类。

“这个类可以让你监控一个目录的变化,并在某些东西被修改时触发一个事件。”

然后,您的代码可以处理事件并处理文件。

代码源 - MSDN:

// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = args[1];

/* Watch for changes in LastAccess and LastWrite times, and
   the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
   | NotifyFilters.FileName | NotifyFilters.DirectoryName;

// Only watch text files.
watcher.Filter = "*.txt";

// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
于 2012-10-22T15:33:25.393 回答
1

一般来说,您会让操作系统负责跟踪文件是否已更改。

如果您使用:

File.GetAttributes

并检查存档标志,这将告诉您文件自上次存档以来是否已更改。我相信 XCOPY 和类似的东西在完成复制后会重置这个标志,但你可能需要自己处理这个问题。

您可以使用以下命令轻松测试 DOS 中的标志:

dir /aa yourfilename

或者只是在 Windows 资源管理器中添加属性列。

于 2012-10-22T15:50:00.960 回答
1

备份程序通常使用文件归档标志来检查文件是否需要备份。当 Windows 修改或创建文件时,它会设置存档标志(请参阅此处)。检查是否设置了归档标志来决定文件是否需要备份:

if ((File.GetAttributes(fileName) & FileAttributes.Archive) == FileAttributes.Archive)
{
    // Archive file.
}

备份文件后,清除存档标志:

File.SetAttributes(fileName, File.GetAttributes(fileName) & ~FileAttributes.Archive);

这假定没有其他程序(例如,系统备份软件)正在清除存档标志。

于 2012-10-22T15:54:40.240 回答
0

从这篇文章中得到 Crc32 类 在 C# 和 .NET 中计算 CRC-32

将您的文件路径传递给此函数... 它返回一个 CRC 值...将其与您已经存在的文件进行比较...如果 CRC 不同,则文件已更改。

internal Int32 GetCRC(string filepath)
{
    Int32 ret = 0;
    StringBuilder hash = new StringBuilder();
    try
    {
        Crc32 crc32 = new Crc32();
                
        using (System.IO.FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.None))
            foreach (byte b in crc32.ComputeHash(fs)) hash.Append(b.ToString("x2").ToLower());
                
        ret = Int32.Parse(hash.ToString(), System.Globalization.NumberStyles.HexNumber);
    }
    catch (Exception ex)
    {
        string msg = (ex.InnerException == null) ? ex.Message : ex.InnerException.Message;
        Console.WriteLine($"FILE ERROR: {msg}");
        
        ret = 0;
    }
    finally
    {
        hash.Clear();
        hash = null;
    }
            
    return ret;
}
于 2021-09-03T19:01:10.307 回答