欢迎光临!
若无相欠,怎会相见

Delphi-改进获取文件MD5 Hash方法

序言

之前所说的获取文件MD5方法有性能问题,没多久我就遇到了,程序假死,卡顿。因此将获取文件MD5的方法改了一下,并测试了一下,大概性能提升了5倍,获取同一个文件的MD5,老方法用时是新方法的6倍。

改进

原始获取文件MD5的方法:

// uses IdHashMessageDigest
Function StreamToMD5(s: TFileStream): string;
var
  MD5Encode: TIdHashMessageDigest5;
begin
  MD5Encode := TIdHashMessageDigest5.Create;
  try
    result := MD5Encode.HashStreamAsHex(s);
  finally
    MD5Encode.Free;
  end;
end;

改进后的方法:

// uses System.Hash
Function GetFileHashMD5(FileName: String): String;
var
  HashMD5: THashMD5;
  BufLen, Readed: integer;
  Stream: TFileStream;
  Buffer: Pointer;

begin
  HashMD5 := THashMD5.Create;
  BufLen := 16 * 1024;
  Buffer := AllocMem(BufLen);
  try
    Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
    try
      while Stream.Position < Stream.size do
      begin
        Readed := Stream.Read(Buffer^, BufLen);
        if Readed > 0 then
        begin
          HashMD5.update(Buffer^, Readed);
        end;
      end;
    finally
      Stream.Free;
    end;
  finally
    FreeMem(Buffer)
  end;

  result := HashMD5.HashAsString.ToUpper;
end;

测试结果

文件大小 原方法用时 新方法用时
1KB 00:00:00.013 00:00:00.001
10KB 00:00:00.020 00:00:00.001
100KB 00:00:00.010 00:00:00.001
1MB 00:00:00.069 00:00:00.008
10MB 00:00:00.455 00:00:00.091
100MB 00:00:04.338 00:00:00.771
1GB 00:00:36.306 00:00:07.451
2GB 00:01:13:218 00:00:15:101
3GB 00:02:19:977 00:00:23:119
4GB 00:02:25:502 00:00:30:106

以上测试结果肯定有偏差,因此将测试程序源码附上,以供需要的参考,程序界面:

unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Hash, IdHashMessageDigest,
  Vcl.StdCtrls, System.Math, IdGlobalProtocols;

type
  TForm2 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;
  Function TransFloatToStr(Avalue: Double; ADigits: integer): String;

implementation

{$R *.dfm}

Function StreamToMD5(s: TFileStream): string;
var
  MD5Encode: TIdHashMessageDigest5;
begin
  MD5Encode := TIdHashMessageDigest5.Create;
  try
    result := MD5Encode.HashStreamAsHex(s);
  finally
    MD5Encode.Free;
  end;
end;

Function GetFileHashMD5(FileName: String): String;
var
  HashMD5: THashMD5;
  BufLen, Readed: integer;
  Stream: TFileStream;
  Buffer: Pointer;

begin
  HashMD5 := THashMD5.Create;
  BufLen := 16 * 1024;
  Buffer := AllocMem(BufLen);
  try
    Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
    try
      while Stream.Position < Stream.size do
      begin
        Readed := Stream.Read(Buffer^, BufLen);
        if Readed > 0 then
        begin
          HashMD5.update(Buffer^, Readed);
        end;
      end;
    finally
      Stream.Free;
    end;
  finally
    FreeMem(Buffer)
  end;

  result := HashMD5.HashAsString.ToUpper;
end;


function TransBytesToSize(Bytes: integer): String;
var
  temp: String;
begin
  if Bytes < 1024 then { 字节 }
  begin
    result := IntToStr(Bytes) + ' Byte';
  end

  else if Bytes < 1024 * 1024 then { KB }
  begin
    temp := TransFloatToStr(Bytes / 1024, 2);
    result := temp + ' KB';
  end

  else if Bytes < 1024 * 1024 * 1024 then { MB }
  begin
    temp := TransFloatToStr(Bytes / (1024 * 1024), 2);
    result := temp + ' MB';
  end

  else { GB }
  begin
    temp := TransFloatToStr(Bytes / (1024 * 1024 * 1024), 2);
    result := temp + ' GB';
  end
end;


Function TransFloatToStr(Avalue: Double; ADigits: integer): String;
var
  v: Double;
  p: integer;
  e: String;
begin
  if abs(Avalue) < 1 then
  begin
    result := FloatToStr(Avalue);
    p := Pos('E', result);
    if p > 0 then
    begin
      e := copy(result, p, length(result));
      setlength(result, p - 1);
      v := RoundTo(StrToFloat(result), -ADigits);
      result := FloatToStr(v) + e;
    end
    else
      result := FloatToStr(RoundTo(Avalue, -ADigits));
  end
  else
    result := FloatToStr(RoundTo(Avalue, -ADigits));
end;


procedure TForm2.Button1Click(Sender: TObject);
var
  path: string;
  FileName: string;
  MD5: string;
  bytes: Integer;
  size: string;
  d1 : TDateTime;
begin
  if OpenDialog1.Execute then
  begin
    FileName := ExtractFileName(OpenDialog1.FileName);
    path := OpenDialog1.FileName;
    d1 := Now();
    MD5 := StreamToMD5(TFileStream.Create(path, fmOpenRead or fmShareDenyWrite));
    Label4.Caption := FormatDateTime('hh:nn:ss.zzz', (Now-d1));
    bytes := FileSizeByName(path);
    size := TransBytesToSize(bytes);
    Label2.Caption := MD5;
    Label3.Caption := size;
  end;

end;

procedure TForm2.Button2Click(Sender: TObject);
var
  path: string;
  FileName: string;
  MD5: string;
  bytes: Integer;
  size: string;
  d2: TDateTime;
begin
  if OpenDialog1.Execute then
  begin
    FileName := ExtractFileName(OpenDialog1.FileName);
    path := OpenDialog1.FileName;
    d2 := Now();
    MD5 := GetFileHashMD5(path);
    Label8.Caption := FormatDateTime('hh:nn:ss.zzz', (Now-d2));
    bytes := FileSizeByName(path);
    size := TransBytesToSize(bytes);
    Label6.Caption := MD5;
    Label7.Caption := size;

  end;
end;

end.

结语

这个方法应该还有进一步改进的可能,但现在时间太晚,后面找时间在尝试提升性能。

如有错误,敬请指出,感谢指正!     — 2020-07-26 00:31:09

赞(0) 打赏
转载请注明:飘零博客 » Delphi-改进获取文件MD5 Hash方法
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

欢迎光临