DELPHI 文件自动备份工具源码分享

发布时间 2023-08-19 20:08:32作者: 一曲轻扬

一.工具界面如下(没做UI)

 

二.设计视图如下:

 三.控件说明:

  1.使用到了第三方StyleControl控件中的SCGPLabel,SCGPEdit,SCGPCheckBox,SCGPCombobox,SCGPButton,这几个控件如果需要,可替换成秕自带控件.难度-1

  2.TrayIcon 这个是让程序缩小到托盘上面去的.

  3.PopupMenu 菜单控件.程序缩小到托盘上时,右键可弹出菜单,如下图  

   4.其他控件,略

 

四.功能说明

   1: 定时备份到指定目录

    2:勾选检查MD5值时,会对最近一次备份的文件与目标文件做MD5文件比较,如果MD5值不一致,则进行备份操作.否则不备份

   3.勾选开机启动可以开机自动运行

   4.记忆用户设置.关闭程序时,ini文件保存到程序目录下.启动时读取此ini文件,恢复用户设置

   5.点击开始后,自动缩小到托盘.托盘右键有菜单.或者双击托盘图标可显示程序界面

 6.目前时间间隔只支持30分钟/1小时/3小时/6小时/12小时.暂时不支持自定义,有朋友需要的话可以二次开发,源码会在下面分享

五.源码分享

unit FMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.ExtCtrls, scControls, scGPControls, scGPExtControls, Vcl.StdCtrls,
  Vcl.Mask, Vcl.Menus, System.ImageList, Vcl.ImgList, FileCtrl,
  IdHashMessageDigest, IOUtils, IdHash, DateUtils, System.Types, Registry,
  IniFiles, IdGlobal;

type
  TMD5 = class(TIdHashMessageDigest5);   //注意这里

  TForm2 = class(TForm)
    scGPEdit1: TscGPEdit;
    scGPLabel1: TscGPLabel;
    scGPLabel2: TscGPLabel;
    scGPComboBox1: TscGPComboBox;
    BeginButton: TscGPButton;
    OpenDialog1: TOpenDialog;
    Timer1: TTimer;
    scGPEdit2: TscGPEdit;
    scGPLabel3: TscGPLabel;
    scGPCheckBox1: TscGPCheckBox;
    TrayIcon1: TTrayIcon;
    PopupMenu1: TPopupMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    N3: TMenuItem;
    ImageList1: TImageList;
    scGPCheckBox2: TscGPCheckBox;
    procedure scGPEdit2RightButtonClick(Sender: TObject);
    procedure scGPEdit1RightButtonClick(Sender: TObject);
    procedure BeginButtonClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure N2Click(Sender: TObject);
    procedure N3Click(Sender: TObject);
    procedure N1Click(Sender: TObject);
    procedure scGPCheckBox2Click(Sender: TObject);
    procedure TrayIcon1DblClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
                { Private declarations }
  public
                { Public declarations }
    procedure AppendRecordToFile(RecordContent: string);
    procedure SetStartupRegistry(EnableStartup: Boolean);
    procedure SaveSettings(const FileName: string; FilePath, FolderPath: string);
    procedure LoadSettings(const FileName: string; var FilePath, FolderPath: string);
    function GetLatestFileMD5(const filePath, folderPath: string): string;
    function StreamToMD5(const FilePatn: string): string;
  end;

var
  Form2: TForm2;
  filePath: string;     //文件路径
  FolderPath: string;    //备份目录

implementation

{$R *.dfm}

procedure TForm2.BeginButtonClick(Sender: TObject);
var
  i: Cardinal;
begin
  if BeginButton.Caption = '停止' then
  begin
    AppendRecordToFile(FormatDateTime('yyyymmdd hhmmss', Now()) + ' 任务被停止');
    BeginButton.Caption := '开始';
    Timer1.Enabled := false;
  end
  else
  begin
    //检查是否已指定文件与保存目录
    filePath := scGPEdit1.Text;
    FolderPath := scGPEdit2.Text;
    if (filePath = '') or (FolderPath = '') then
    begin
      ShowMessage('请先选择要备份的文件与存放备份文件的目录');
      exit;
    end;
    //如果文件名不存在,则提示文件名不存在,然后退出程序
    if not FileExists(filePath) then
    begin
      ShowMessage('指定的 文件 不存在,请检查');
      exit;
    end;
    //检查目录是否存在
    if not DirectoryExists(FolderPath) then
    begin
      ShowMessage('指定的 文件夹路径 不存在,请检查');
      exit;
    end;

    BeginButton.Caption := '停止';
    case scGPComboBox1.ItemIndex of
      0:
        i := 30 * 60 * 1000;
      1:
        i := 60 * 60 * 1000;
      2:
        i := 3 * 60 * 60 * 1000;
      3:
        i := 6 * 60 * 60 * 1000;
      4:
        i := 12 * 60 * 60 * 1000;
    end;

    AppendRecordToFile(FormatDateTime('yyyy-mm-dd hh:mm:ss', Now()) + '  开始执行备份任务;');
    Timer1.Interval := i;     //设置时间间隔 ,测试时写个几秒即可
    Timer1.Enabled := True;   //启动计时器
    //隐藏到托盘
    TrayIcon1.Visible := True; // 显示托盘图标
    TrayIcon1.PopUpMenu := PopupMenu1; // 将弹出菜单关联到托盘图标上
    Self.Hide;
  end;
end;

procedure TForm2.scGPCheckBox2Click(Sender: TObject);   //选择备份目录
begin
  if scGPCheckBox2.STATE = cbChecked then
    SetStartupRegistry(TRUE) //设置开机启动
  else
    SetStartupRegistry(false) //取消开机启动
end;

procedure TForm2.scGPEdit1RightButtonClick(Sender: TObject);
begin
  OpenDialog1.Options := [ofHideReadOnly, ofFileMustExist];    //对话框设置:隐藏只读文件,文件必须存在.不需要的可以注释掉
  if OpenDialog1.Execute then
    scGPEdit1.Text := OpenDialog1.FileName;   //返回用户选中的文件路径到文本框
end;

procedure TForm2.scGPEdit2RightButtonClick(Sender: TObject);
var
  Dir: string;
begin
  if SelectDirectory('请选择目录 ', ' ', Dir) then // Dir存储着用户选择的目录名称
    scGPEdit2.Text := Dir;        //返回用户选中的文件夹路径到文本框
end;

procedure TForm2.Timer1Timer(Sender: TObject);   //计时器
var
  NewMD5, OldMD5: string;
  FileStream: TFileStream;
  MD5Hash: TIdHashMessageDigest5;
  logStr: string;
begin
  logStr := FormatDateTime('yyyymmdd hhmmss', Now());
  if scGPCheckBox1.State = cbChecked then //如果勾选了检查MD5值
  begin
   //取到目标文件的MD5值
    FileStream := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyNone);
    try
      MD5Hash := TIdHashMessageDigest5.Create;
      try
        NewMD5 := MD5Hash.HashStreamAsHex(FileStream);
      finally
        MD5Hash.Free;
      end;
    finally
      FileStream.Free;
    end;
    AppendRecordToFile('目标文件MD5值:' + NewMD5);    //输出日志到备份日志
    //获取到最近备份文件的MD5值 .
    OldMD5 := GetLatestFileMD5(FilePath, FolderPath);
    AppendRecordToFile('最新文件MD5值:' + OldMD5);
    //如果OldMD5的值等于NewMD5值,说明数据库没有变化,不需要进行备份
    if NewMD5 = OldMD5 then
    begin
      AppendRecordToFile(logStr + 'MD5值未发生改变,不进行备份操作');
      exit;
    end
    else
      AppendRecordToFile(logStr + 'MD5值有变动,进行备份操作');
  end;

  //开始备份
  //TPath.Combine 方法来组合目标目录 FolderPath 和新的文件名。这样可以确保目标文件的完整路径,它会自动加"\"。
  //使用 TPath.GetExtension 方法获取源文件的扩展名,并将其与新的文件名进行组合。
  TFile.Copy(filePath, TPath.Combine(FolderPath, logStr + TPath.GetExtension
    (filePath)));
  AppendRecordToFile(logStr + ' 备份完成');
end;

procedure TForm2.TrayIcon1DblClick(Sender: TObject);  //双击时显示界面
begin
  self.Show;
end;

procedure TForm2.AppendRecordToFile(RecordContent: string);   //追加文本信息到日志文件
var
  FullPath: string;
  FileWriter: TStreamWriter;
begin
  FullPath := TPath.Combine(FolderPath, '备份日志.txt');
  FileWriter := TStreamWriter.Create(FullPath, FileExists(FullPath));
  try
    FileWriter.WriteLine(RecordContent);
  finally
    FileWriter.Free;
  end;
end;

function TForm2.GetLatestFileMD5(const filePath, folderPath: string): string;  //找到最近备份文件,并返回MD5
var
  FileType: string;
  SearchRec: TSearchRec;
  CreateTime, LatestCreateTime: TDateTime;
  FullPath, LatestFilePath: string;
begin
  Result := '';
  FileType := '*' + TPath.GetExtension(filePath); // 获取文件扩展名,与*构成模糊匹配
  FullPath := TPath.Combine(folderPath, FileType);  //带路径的文件名
  //第一个文件,默认它是最新的文件,然后再慢慢遍历
  if FindFirst(FullPath, faAnyFile, SearchRec) = 0 then
  begin
    FullPath := TPath.Combine(folderPath, SearchRec.Name);
    LatestCreateTime := Tfile.GetCreationTime(FullPath);   //文件的创建时间
    LatestFilePath := FullPath;                              //文件路径
    // 遍历文件夹中的文件
    while FindNext(SearchRec) = 0 do
    begin
      // 比较文件的创建时间,并更新最新文件的信息
      FullPath := TPath.Combine(folderPath, SearchRec.Name);
      CreateTime := Tfile.GetCreationTime(FullPath);   //文件的创建时间
      if CreateTime > LatestCreateTime then    //比较两个文件的创建时间
      begin
        LatestFilePath := FullPath;   //记录更近的备份文件的文件名
        LatestCreateTime := CreateTime;  //  记录更近的备份文件的创建时间
      end;
    end;

    // 将最新文件的路径传递给 StreamToMD5() 函数来计算 MD5
    Result := StreamToMD5(LatestFilePath);
  end;

  FindClose(SearchRec);  //关闭记录类型
end;

procedure TForm2.N1Click(Sender: TObject);  //显示 菜单
begin
  Self.Show;
end;

procedure TForm2.N2Click(Sender: TObject);  //关于 菜单
begin
  ShowMessage('制作人:甘兴鹏 QQ:452428745')
end;

procedure TForm2.N3Click(Sender: TObject);   //退出 菜单
begin
  TrayIcon1.Visible := False;   //关闭前,设置托盘不可见,不然退出了还会有个图标显示在那里
  Close;
end;

procedure TForm2.SetStartupRegistry(EnableStartup: Boolean);  //设置开机启动
var
  Reg: TRegistry;
begin
  //ExtractFileName(ParamStr(0)) 来获取当前程序的文件名作为注册表键名和值。
  //ParamStr(0) 返回当前程序的完整路径
  //ExtractFileName 函数提取出程序的文件名部分。
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_CURRENT_USER;
    if Reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Run', True) then   //仅于WIN10测试
    begin
      if EnableStartup then
        Reg.WriteString(ExtractFileName(ParamStr(0)), ParamStr(0)) // 将程序路径设置为开机启动项的值,键名为程序名称
      else
        Reg.DeleteValue(ExtractFileName(ParamStr(0))); // 删除开机启动项的值,键名为程序名称
      Reg.CloseKey;
    end;
  finally
    Reg.Free;
  end;
end;
// 保存设置到配置文件

procedure TForm2.SaveSettings(const FileName: string; FilePath, FolderPath: string);
var
  IniFile: TIniFile;
begin
  IniFile := TIniFile.Create(FileName);
  try
    IniFile.WriteString('Settings', 'FilePath', scGPEdit1.Text);
    IniFile.WriteString('Settings', 'FolderPath', scGPEdit2.Text);
  finally
    IniFile.Free;
  end;
end;

// 从配置文件中读取设置
procedure TForm2.LoadSettings(const FileName: string; var FilePath, FolderPath: string);
var
  IniFile: TIniFile;
begin
  IniFile := TIniFile.Create(FileName);
  try
    scGPEdit1.Text := IniFile.ReadString('Settings', 'FilePath', '');
    scGPEdit2.Text := IniFile.ReadString('Settings', 'FolderPath', '');
  finally
    IniFile.Free;
  end;
end;

// 在程序启动时自动读取设置
procedure TForm2.FormCreate(Sender: TObject);
var
  AppPath: string;
  ConfigFile: string;
  FilePath, FolderPath: string;
begin
  AppPath := ExtractFilePath(Application.ExeName);
  ConfigFile := AppPath + 'config.ini';
  FilePath := scGPEdit1.Text;
  FolderPath := scgpEdit2.Text;
  if FileExists(ConfigFile) then
    LoadSettings(ConfigFile, FilePath, FolderPath);
end;

// 在程序关闭时保存设置
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
var
  AppPath: string;
  ConfigFile: string;
begin
  AppPath := ExtractFilePath(Application.ExeName);
  ConfigFile := AppPath + 'config.ini';

  SaveSettings(ConfigFile, scGPEdit1.Text, scgpEdit2.Text);
end;

function TForm2.StreamToMD5(const FilePatn: string): string;   //取文件的MD5值
var
  MD5Encode: TMD5;
  filesen: TFileStream;
begin
  MD5Encode := TMD5.Create;
  try
    filesen := TFileStream.Create(FilePatn, fmopenread or fmshareExclusive);
    result := MD5Encode.HashStreamAsHex(filesen);
  finally
    MD5Encode.Free;
    filesen.Free
  end;
end;

end.