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

将项目文件备份到 Gmail

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (38投票s)

2009年5月1日

CDDL

6分钟阅读

viewsIcon

77693

downloadIcon

2025

一个 WinForms 应用程序,支持命令行执行,可以扫描文件夹树,创建(一个或多个)Zip 文件,并将其连同格式化的消息一起发送到 Gmail 账户。

引言

这是一个简单的应用程序,用于将您的代码备份到 Gmail。它会创建(一个或多个)Zip 文件,其中包含经过过滤的文件列表,排除 objbinexe、SVN 以及其他非必需文件。如果需要,它会分割成多个 Zip 文件,您可以控制 Zip 文件的大小。

背景

像大多数开发者一样,我使用 SVN 作为代码仓库,并尽量频繁地进行备份。我喜欢一个自动化、每日、异地备份的想法,这样我就可以随时随地获取我的代码文件。因此,我开发了这个小工具,以确保我对我的重要文件进行完整、每日异地备份,其中包括所有 .cs.vb.proj 等文件。通过备份到 Gmail,即使我不在网络上(例如在客户那里),我也可以访问我所有的源代码。这不是增量备份,尽管您可以轻松修改程序使其区分日期。

通过每天运行此程序,我还可以轻松地恢复给定日期的开发树的快照,这在过去几次派上了用场。(我知道您可以从 SVN 中拉取先前的版本,但相信我,这要容易得多!)

将代码备份到 Gmail 可能看起来是一项微不足道的任务,但它比简单地 Xcopy 一堆文件要复杂一些。关键在于排除大多数重量级文件:我不需要备份任何 obj 文件、本地引用副本、.svn 文件、.EXE 或 PDB 文件。我的文件夹里还有很多第三方文件不需要包含在内。因此,该程序允许您定义包含和排除您需要和不需要的文件模式的列表。您可以指定要排除的文件夹(例如 \bin)或特定的文件模式(例如 *.zip)。您的设置保存在一个 XML 文件中,该文件只是程序中 BackupVars 类的序列化形式。

Gmail 限制

Gmail 是一个了不起的电子邮件服务。我认为它们现在为您提供了大约 15GB 的空间... 这使其非常适合此类任务。但是,它确实有一些限制:您不能将任何 .exe 文件包含在附件中,即使文件在 Zip 文件内。Backup2gmail 程序会将 Zip 文件重命名为 xxx_yymmdd_hhmmss.ziprenamed,以便 Gmail 只将其视为二进制文件。

Gmail 将总消息大小限制为 20MB,这相当大。然而,我的源代码树,加上一些测试数据,压缩后会超过 20MB。因此,该程序将创建多个 Zip 文件,并将每个 Zip 文件作为单独的电子邮件发送。电子邮件的主题将显示消息编号,邮件正文将包含一个格式精美的 HTML 表格,列出文件。

在 Gmail 中,电子邮件看起来是这样的

要恢复您的文件,您需要手动下载附件,将其重命名回 .zip 文件,然后解压缩。当然,文件夹在 Zip 文件中会被保留。

命令行用法

此程序可以从命令行运行,也可以交互式运行。程序代码包含一个命令行解析库。可以使用两种命令行格式:您可以传入文件夹名称、电子邮件账户和密码,或者您可以传入一个 Backup2Gmail 配置文件名。B2G 配置文件是一个包含设置和电子邮件凭据的 XML 文件。

单击“文件”菜单下的“批量命令行”菜单选项以获取命令行格式。

在任务计划程序中安排程序

您可以轻松地在 Windows 任务计划程序中设置计划,以实现夜间自动备份。我通常在凌晨运行作业。程序不包含计划功能... 您需要手动完成。

Using the Code

源代码中有许多有用的项,您可能会发现它们有益

双模式操作 - 命令行或交互式

有一个创建可以从命令行运行的 WinForms 应用程序的技巧。默认情况下,WinForms 应用程序会创建一个主窗口,因此为了避免在使用命令行时出现一个幽灵窗口,您可以在启动时使用以下代码

class Backup2Gmail
{
    [DllImport("kernel32.dll")]
    static extern bool AttachConsole(int dwProcessId);
    private const int ATTACH_PARENT_PROCESS = -1;

    [STAThread]
    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            frmBackup oBackup = new frmBackup();
            oBackup.ShowDialog();
            return;
        }

        AttachConsole(ATTACH_PARENT_PROCESS);

        CommandLineParser parser = new CommandLineParser();
        Backup2GmailArgs oBackupArgs = new Backup2GmailArgs();
        parser.ExtractArgumentAttributes(oBackupArgs);
        try
        {
            parser.ParseCommandLine(args);

        }
        catch (Exceptions.CommandLineArgumentException exC)
        {

            Console.Write(exC.ToString());

        }

对我来说,另一个挑战是创建一个没有依赖项的单个可执行文件。我选择使用 CodePlex 的 DotNetZip 库(参见 http://dotnetzip.codeplex.com/) 而不是更常用的 SharpZip 库。这个库更容易集成到项目中,并且可以编译成最终的可执行文件。

使用 System.Net.Mail.SmtpClient 库发送电子邮件。以下是发送电子邮件的代码

System.Net.Mail.Attachment mailAttachment = 
   new System.Net.Mail.Attachment(oBackupVars.Outputfile);

mm.Attachments.Add(mailAttachment);

string smtpHost = "smtp.gmail.com";
//string userName = GmailAccount;//sending Id
//string password = GmailPassword;
System.Net.Mail.SmtpClient mClient = new System.Net.Mail.SmtpClient();
mClient.Port = 587;
mClient.EnableSsl = true;
mClient.UseDefaultCredentials = false;
mClient.Credentials = new System.Net.NetworkCredential(oBackupVars.Userid, 
                      oBackupVars.Password);
mClient.Host = smtpHost;
mClient.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
mClient.Timeout = int.MaxValue;
ShowMessage(oBackupVars, "Sending email to " + 
            oBackupVars.Userid + "  " + mm.Subject);
mClient.Send(mm);
ShowMessage(oBackupVars, "Sent email with backup zip file to " + 
            oBackupVars.Userid);

请注意,Gmail 使用非标准端口号 (587)。

程序设置和运行时统计信息

可序列化的 BackupVars 类包含所有运行时统计信息、电子邮件设置以及控制哪些文件包含或排除在备份中的过滤器。该类中的值使用 Windows PropertyGrid 控件进行编辑。序列化使用 .NET 序列化器完成。

[TypeConverter(typeof(ExpandableObjectConverter))]
public class BackupVars
{

    [XmlIgnore()]
    public ZipFile oZipFile = null;

    [XmlIgnore()]
    public int NumEmailMessages = 0;


    [XmlIgnore()]
    public long UncompressedLength = 0;

    [XmlIgnore()]
    public long CompressedFileLength = 0;

    [XmlIgnore()]
    public Label StatusMessage = null;

    [XmlIgnore()]
    public bool IsRunning = false;

    [XmlIgnore()]
    public bool IsCancelled = false;

    private string _TempFolderForZipFiles = @"C:\Temp\Backup2Gmail\";
    public string TempFolderForZipFiles
    {
        get { return _TempFolderForZipFiles; }
        set { _TempFolderForZipFiles = value; }
    }

    private string _userid;
    public string Userid
    {
        get { return _userid; }
        set { _userid = value; }
    }
    private string _password;

    public string Password
    {
        get { return _password; }
        set { _password = value; }
    }

    private string _outputfile;
    public string Outputfile
    {
        get { return _outputfile; }
        set { _outputfile = value; }
    }
    private string _SourceCodeFolderBaseName;
    public string SourceCodeFolderBaseName
    {
        get { return _SourceCodeFolderBaseName; }
        set { _SourceCodeFolderBaseName = value; }
    }

    private string _RootPath;
    public string RootPath
    {
        get { return _RootPath; }
        set { _RootPath = value; }
    }
    public StringBuilder sb = new StringBuilder();


    private string[] _IncludedFilePattern = new string[] { "*.*" };
    public string[] IncludeFilePatterns
    {
        get { return _IncludedFilePattern; }
        set { _IncludedFilePattern = value; }
    }

    private string[] _ExcludedFilePattern = new string[] { "*.msi*" };
    public string[] ExcludedFilePattern
    {
        get { return _ExcludedFilePattern; }
        set { _ExcludedFilePattern = value; }
    }

    private string[] _ExcludedExtensionStartsWith = 
            new string[] { ".zip", ".msi" };
    public string[] ExcludedExtensionStartsWith
    {
        get { return _ExcludedExtensionStartsWith; }
        set { _ExcludedExtensionStartsWith = value; }
    }

    private string[] _ExcludedExtensionExactMatch = 
      new string[] { ".exe", ".dll", ".pdb", ".log" };
    public string[] ExcludedExtensionExactMatch
    {
        get { return _ExcludedExtensionExactMatch; }
        set { _ExcludedExtensionExactMatch = value; }
    }

    private string[] _ExcludedFoldersContains = new string[] { 
                @"\debug\bin", 
                @"\release", 
                @"pro\actmatewebsite\actmate\backup", 
                @"pro\actmatewebsite\actmate\recovery" };
    public string[] ExcludedFoldersContains
    {
        get { return _ExcludedFoldersContains; }
        set { _ExcludedFoldersContains = value; }
    }

    private string[] _ExcludedExactFolderNames = new string[] { 
                @"bin", 
                @"obj",
                @"References",
                @".svn", 
                @"RadControls", 
                @"fckeditor"};

    public string[] ExcludedExactFolderNames
    {
        get { return _ExcludedExactFolderNames; }
        set { _ExcludedExactFolderNames = value; }
    }

    private long _ZipFilesize = 19500000;
    public long ZipFilesize
    {
        get { return _ZipFilesize; }
        set { _ZipFilesize = value; }
    }

    public BackupVars()
    {

    }

    public BackupVars Clone()
    {
        return DeserializeFromXMLString(SerializeToXMLString(this));
    }

    public static BackupVars DeserializeFromXMLString(string in_Backup2GmailXML)
    {
        XmlSerializer s = new XmlSerializer(typeof(BackupVars));
        return (BackupVars)s.Deserialize(new StringReader(in_Backup2GmailXML));
    }

    public static string SerializeToXMLString(BackupVars in_Backup2Gmail)
    {
        try
        {
            System.Xml.Serialization.XmlSerializer s = 
              new System.Xml.Serialization.XmlSerializer(typeof(BackupVars));
            StringWriter sw = new StringWriter();
            s.Serialize(sw, in_Backup2Gmail);
            return sw.ToString();
        }
        catch (Exception ex)
        {
            throw new Exception("Could not serialize " + 
                  "Backup2Gmail. Error Msg: " + ex.Message, ex);
        }
    }
}

如果您不熟悉序列化,您可能想看看我在这个类中使用的技术。您会看到一些属性带有 [XMLIgnore()] 属性 - 这将指示序列化器在 XML 文件中排除该属性。

此类中的关键属性是

  • 您要备份的根文件夹
  • Gmail 账户
  • Gmail 密码
  • 包含和排除规则

您可能想在文件中硬编码一些排除和包含规则来处理您的情况。文件过滤是通过包含和排除模式的组合完成的

  • IncludeFilePatterns
  • ExcludedFilePattern
  • ExcludedExactFolderNames
  • ExcludedFoldersContains
  • ExcludedExtensionStartsWith
  • ExcludedExtensionExactMatch

关注点

我最初尝试使用 .NET 中内置的 Zip 工具,但在花费了太多时间试图理解 Redmond 的技术人员的想法之后,我改用了更符合逻辑的 DotNetZip 库。

此程序主要面向需要备份代码的程序员。但是,您也可以将其用于压缩后小于 20MB 的文档和其他文件。它不打算成为一个完整的备份系统,如果您尝试备份大型文件(如 SQL Server 文件),它将无法正常工作。

历史

  • 版本 2.0 - CodeProject 上传 - 2009 年 5 月 1 日。
© . All rights reserved.