65.9K
CodeProject 正在变化。 阅读更多。
Home

实现备份轮换方案的备份工具 (C# 的开源 Windows Forms 应用程序)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (12投票s)

2018年1月7日

CPOL

6分钟阅读

viewsIcon

42726

downloadIcon

2057

CopyTree 是一款实现备份轮换方案的备份工具。它是一个用 C# 编写的开源 Windows Forms 应用程序。该工具旨在将关键文件夹备份到多个备份文件夹。

Copy Tree Icon

引言

CopyTree 是一款实现备份轮换方案的备份工具。它是一个用 C# 编写的开源 Windows Forms 应用程序。该工具旨在将关键文件夹备份到多个备份文件夹。运行该工具的结果是,每个备份文件夹都是源文件夹在给定时间的精确副本。每次运行该工具时,最旧的备份文件夹都会用当前的源文件夹进行更新。换句话说,该工具在备份文件夹之间轮换。备份更新过程确保备份目录结构与源目录相同。新文件从源复制到备份。已删除的文件将从备份中删除。已修改的文件将覆盖旧版本。未修改的文件将被忽略。除了第一次,结果是一个非常高效的备份过程。

安装该工具后,您选择硬盘上需要备份的关键文件夹。我们称它们为源文件夹。接下来,您在外置硬盘或多个硬盘上创建几个备份文件夹。我选择三个备份文件夹。每次运行该工具时,它都会将源文件夹备份到这些备份文件夹中最旧的一个。结果是,在任何时候,您都有源文件夹的三代副本。例如,如果您每天结束时运行该工具一次,那么第二天您将拥有昨天、前天和三天前的副本。

复制过程将源文件夹结构与备份文件夹结构进行比较。如果文件或子文件夹存在于源中但不存在于备份中,则该工具将其从源复制到备份。如果文件或子文件夹存在于备份中但不存在于源中,则该工具将其从备份中删除。如果文件同时存在于源和备份中,则该工具比较源和备份的 LastWriteTimeUtc。如果它们相等,则该工具假定自上次备份以来文件未被修改,并且不进行复制。如果最后写入时间不同,则该工具复制源文件并覆盖旧备份。

文件复制过程保留文件 CreationTimeUtcLastWriteTimeUtcLastAccessTimeUtc。此外,文件属性标志:ReadOnlyHiddenSystemArchiveNormal 将被保留。如果文件是只读的,并且需要删除或覆盖它,则只读标志将被删除。计算机上其他应用程序正在使用的源文件无法复制。异常将由 try-catch 对捕获,并将显示并记录错误消息。备份过程将继续。

备份方案

备份方案由两个列表组成。备份列表和源列表。每个列表都由包含两个字段的记录组成。

  • 备份根文件夹
    • 上次备份日期和时间
    • 备份根文件夹名称
  • 备份子文件夹名称和源文件夹路径
    • 备份子文件夹名称
    • 源文件夹路径

下面是备份根文件夹列表的示例。该列表包含外部硬盘驱动器 G: 上的三代备份轮换。

  • 2018/01/01 17:12:20 G:\Backup1
  • 2018/01/02 17:35:43 G:\Backup2
  • 2018/01/03 18:01:05 G:\Backup3

假设您想备份存储在文件夹 C:\MyDevelopment 中的开发项目。并且您想备份您的私人文档文件夹 C:\MyDocuments。备份子文件夹名称和源文件夹路径将是

  • MyDevelopment C:\MyDevelopment
  • MyDocuments C:\MyDocuments

如果选择 Backup1 作为当前备份,CopyTree 将把 C:\MyDevelopment 复制到 G:\Backup1\MyDevelopment 文件夹。并将 C:\MyDocuments 复制到 G:\Backup1\MyDocuments

Copy Tree Main Screen

CopyTree 主屏幕

Copy Tree Edit Schema

CopyTree 编辑方案屏幕

演示程序

在您的硬盘上创建一个文件夹 CopyTree(或任何其他名称)。将 CopyTree.exe 复制到此文件夹。启动程序。单击“编辑方案”按钮。

将显示“编辑备份方案”窗口表单。添加一个或多个备份根文件夹。我的建议是三个。将建议的上次备份时间保留为当前时间。

添加一个或多个源文件夹路径及其对应的备份子文件夹。

点击 **保存**。

如果所需的备份根目录结构不存在,CopyTree 将请求您的许可来创建它。

Go 执行备份。

备份进度将显示在屏幕顶部的“信息”文本标签中。

屏幕中央的错误日志将显示错误。通常,不会有错误。系统异常最常见的原因是文件正在使用时尝试复制文件。

备份完成后,您可以按 View Log 按钮或 View Errors 按钮查看所有活动。

程序源代码

Backup 类是该工具的核心。backup 类使用 BackgroundWorker 类在后台线程中运行实际的备份过程。

CopyTree 类展示了如何创建 Backup 类,如何启动备份,如何记录结果,以及如何在过程完成时收到通知。

创建 backup 类并附加两个事件处理程序。

// create backup class
Backup Backup = new Backup();
Backup.WriteToLogFile += WriteToLogFile;
Backup.BackupCompletedEvent += OnBackupCompleted;

定义写入日志文件事件处理程序。

/// <summary>
/// Write to log file
/// </summary>
/// <param name="Control">Log or Error</param>
/// <param name="LogText">Message</param>
private void WriteToLogFile
    (
    LogControl Control,  // LogControl is either Log or Error
    string LogText       // text
    )
        {
        // write to loe and error files
        }

定义备份完成事件处理程序。

/// <summary>
/// Backup process is completed
/// </summary>
private void OnBackupCompleted
        (
        bool BackupDone	// true=normal completion, false=user pressed cancel
        )
    {
    // perform completed event tasks
    }

启动备份。

// set the selected index of the backup folder list
Schema.RootIndex = SelectedIndex;

// initiate backup
Backup.BackupFolders(Schema);

Backup 类的主要方法是 PerformFolderBackup。它是一个递归方法,遍历源和 backup 文件夹树。

/// <summary>
/// recursive folder backup
/// </summary>
/// <param name="SourceFolderName">Source folder name</param>
/// <param name="BackupFolderName">Backup folder name</param>
private void PerformFolderBackup
    (
    DirectoryInfo SourceFolderInfo,
    DirectoryInfo BackupFolderInfo
    )
{
// cancel backup
if(BackupWorker.CancellationPending) throw new CanceBackupException();

// backup folder full name shortcut
string BackupFolderFullName = BackupFolderInfo.FullName;

// get source child files
FileInfo[] SourceFiles = SourceFolderInfo.GetFiles();

// get backup child files
FileInfo[] BackupFiles = BackupFolderInfo.GetFiles();

// source has files
if(SourceFiles.Length != 0)
    {
    // backup has files
    if(BackupFiles.Length != 0)
        {
        // backup has files
        EqualizeFiles(SourceFiles, BackupFiles, BackupFolderFullName);
        }

    // backup has no files
    else
        {
        // copy all source files to backup folder
        CopyFiles(SourceFiles, BackupFolderFullName);
        }
    }

// source has no files but backup has files
else if(BackupFiles.Length != 0)
    {
    // delete all files of backup folder
    DeleteFiles(BackupFiles);
    }
	
// get source child folders
DirectoryInfo[] SourceFolders = SourceFolderInfo.GetDirectories();

// get backup child folders
DirectoryInfo[] BackupFolders = BackupFolderInfo.GetDirectories();

// source has folders
if(SourceFolders.Length != 0)
    {
    // backup has folders
    if(BackupFolders.Length != 0)
        {
        // backup has folders
        EqualizeFolders(SourceFolders, BackupFolders, BackupFolderFullName);
        }

    // backup has no folders
    else
        {
        // copy all source child folders to backup folder
        CreateFolders(SourceFolders, BackupFolderFullName);
        }
    }

// source has no folders but backup has folders
else if(BackupFolders.Length != 0)
    {
    // delete all files of backup folder
    DeleteFolders(BackupFolders);
    }
	
// done
return;
}

作者发布的开源软件

历史

  • 2017/08/01:版本 1.0 原始版本
  • 2017/08/09:版本 1.1 更改 DateTime.TryParse 输出参数以与早期 C# 编译器一起使用
  • 2017/08/01:版本 1.2 修复了程序首次使用时 Go 按钮的启用状态问题
  • 2019/06/14:版本 1.3 修复了未经授权的异常
© . All rights reserved.