DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: hxh57738897
今日帖子: 27
在线用户: 8
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/21 14:59:55
标题:
请问有没有方法快速取一个文件的特征码?MD5?SHA? 浏览:3686
加入我的收藏
楼主: 做一个上传管理软件...

如百do网盘..Q盘..都会对上传文件进行校验...

一般使用的 md5 或 sha....小文件没问题,,文件大了就很慢了...

有没有什么方法可以快点?  

如搞点另类方法,如只取 大文件的 前 1M 和后 1M 字节 的 二进制 码来判断 ,
不过我查过,取二进制好像也要load入内存,有没有什么方法直接取二进制码的?

各位可以提供些方法或建议吗? 谢谢...
----------------------------------------------
谢谢各位大大
作者:
男 cnpack (CnPack) ★☆☆☆☆ -
普通会员
2018/3/21 15:25:51
1楼: 内存映射文件的方式可以减少一定开销,但本质上仍然是将数据载入进内存进行计算。

可以参考cnvcl组件包中的CnMD5.pas中的FileMD5函数:

https://github.com/cnpack/cnvcl
----------------------------------------------
欢迎使用CnPack IDE Wizards
http://www.cnpack.org/
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/3/21 15:29:40
2楼: 楼上的 大神。
什么时候支持 TEncoding 啊。
----------------------------------------------
(C)(P)Flying Wang
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2018/3/21 15:37:01
1楼: rsync rolling式Unix/Linux下同步文件的一个算法,里面使用了Alder32 CRC校验算法和MD5算法 ,Alder32 CRC校验比MD5快很多,但也有缺陷就是碰撞率比MD5高,如果不是特别关键的比较,可以使用Alder32位CRC校验。
----------------------------------------------
简单做人,认真做事。
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2018/3/21 15:49:39
3楼: 一直使用的MD5内存映射方式单元:

unit uMD5;

interface

uses
  Winapi.Windows, System.SysUtils;

const
  MD5BlockSize  = 2*1024*1024;

type
  TMD5Digest  = record
    Digest  : array[0..3]of LongWord;
    function ToString: string;
    function CompareWith(const MD5Dig: TMD5Digest): Integer;
    function EqualsTo(const MD5Dig: TMD5Digest): Boolean; inline;
  end;
  PMD5Digest  = ^TMD5Digest;

  TMD5Context = record
    Internals : array[0..21]of LongWord;
    MD5Digest : TMD5Digest;
    procedure Initialize; stdcall;
    procedure Update(const Buffer; Count: LongWord); stdcall;
    procedure FinalDigest; stdcall;
    class function MD5(const Buffer; Size: LongWord): TMD5Digest; static;
    class function MD5HashFile(const FileName: string;
      var MD5Dig: TMD5Digest): Boolean; static;
    constructor Create(const Buffer; Size: LongWord);
  end;

implementation

{ TMD5Digest }

function TMD5Digest.CompareWith(const MD5Dig: TMD5Digest): Integer;
var
  i: Integer;
begin
  for i:=0 to 3 do
  begin
    Result  := Digest[i] - MD5Dig.Digest[i];
    if(Result<>0)then Exit;
  end;
end;

function TMD5Digest.EqualsTo(const MD5Dig: TMD5Digest): Boolean;
begin
  Result  := ( PInt64(@Digest[0])^=PInt64(@MD5Dig.Digest[0])^)and
          (PInt64(@Digest[2])^=PInt64(@MD5Dig.Digest[2])^);
end;

function TMD5Digest.ToString: string;
const HexChars  : array[0..15]of Char = '0123456789ABCDEF';
type  T16Bytes  = array[0..15]of Byte;
var
  i : Integer;
  pSrc  : ^T16Bytes;
  pDst  : PChar;
begin
  SetLength(Result, 32);
  pDst  := Pointer(Result);
  pSrc  := @Digest;
  for i:=0 to 15 do
  begin
    pDst[i*2+0] := HexChars[pSrc[i] div 16];
    pDst[i*2+1] := HexChars[pSrc[i] and $f];
  end;
end;

{ TMD5Context }

const
  libName = 'AdvApi32.dll';
  lib_md5Init = 'MD5Init';
  lib_md5Final  = 'MD5Final';
  lib_md5Update = 'MD5Update';

  function GetFileSizeEx(hFile: THandle; out lpFileSize): LongBool; stdcall;
     external kernel32 name 'GetFileSizeEx';

constructor TMD5Context.Create(const Buffer; Size: LongWord);
begin
  Initialize;
  Update(Buffer, Size);
end;

procedure TMD5Context.FinalDigest; external libName name lib_md5Final;

procedure TMD5Context.Initialize; external libName name lib_md5Init;

class function TMD5Context.MD5(const Buffer; Size: LongWord): TMD5Digest;
var
  ctx : TMD5Context;
begin
  ctx.Initialize;
  ctx.Update(Buffer, Size);
  ctx.FinalDigest;
  Result  := ctx.MD5Digest;
end;

class function TMD5Context.MD5HashFile(const FileName: string;
  var MD5Dig: TMD5Digest): Boolean;
var
  hFile, hMap: THandle;
  pAddr : Pointer;
  nSize : Cardinal;
  nLeft : Int64;
  md5ctx  : TMD5Context;
  nFileSize, nOffset: record
  case Byte of
    0 : ( FullInt: Int64 );
    1 : ( dw32Lo, dw32Hi: DWORD );
  end;
begin
  hFile := CreateFile(
      Pointer(FileName),
      GENERIC_READ,
      FILE_SHARE_READ,
      nil,
      OPEN_EXISTING,
      FILE_ATTRIBUTE_NORMAL,
      0);
  if(hFile=INVALID_HANDLE_VALUE)then
    Exit(False);

  try
    if not(GetFileSizeEx(hFile, nFileSize)) then
      Exit(False);
    hMap  := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
    if(hMap=0)then
      Exit(False);
    nOffset.FullInt := 0;
    try
      md5ctx.Initialize;
      repeat
        nLeft := nFileSize.FullInt - nOffset.FullInt;
        if(nLeft<MD5BlockSize)then
        begin
          nSize := nLeft;
          if(nSize=0)then
          Break;
        end else
          nSize := MD5BlockSize;

        pAddr := MapViewOfFile(hMap, FILE_MAP_READ, nOffset.dw32Hi, nOffset.dw32Lo, nSize);
        md5ctx.Update(pAddr^, nSize);
        UnmapViewOfFile(pAddr);
        Inc(nOffset.FullInt, nSize);
      until False;
    finally
      CloseHandle(hMap);
    end;
  finally
    CloseHandle(hFile);
  end;
  md5ctx.FinalDigest;
  MD5Dig  := md5ctx.MD5Digest;
  Result  := True;
end;

procedure TMD5Context.Update(const Buffer; Count: LongWord);
  external libName name lib_md5Update;

end.

使用也很简单:
var
  md5Data: TMD5Digest;

  if TMD5Context.MD5HashFile('文件路径', md5Data) then
    ShowMessage(md5Data.ToString);
----------------------------------------------
简单做人,认真做事。
作者:
男 cnpack (CnPack) ★☆☆☆☆ -
普通会员
2018/3/21 16:03:57
4楼: 2楼,具体需要在什么地方支持TEncoding?
----------------------------------------------
欢迎使用CnPack IDE Wizards
http://www.cnpack.org/
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/3/21 16:35:00
5楼: 新版本啊。
虽然新版本的 System.Hash 已经带了。
但是如果想用 cnMd5 的时候,就不支持 Encoding 了。
只能用 D7 的古老写法。不喜欢而已。

不过,还是算了,需求比较小。
----------------------------------------------
(C)(P)Flying Wang
作者:
男 earthsbest (全能中间件) ▲▲▲▲△ -
普通会员
2018/3/21 16:41:30
6楼: 有大神封装了一个Delphi 版本的FastMD5,目前见过最快的MD5了。

https://github.com/PassByYou888/FastMD5
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
作者:
男 mricy (Icy) ▲▲▲▲△ -
普通会员
2018/3/21 18:17:14
7楼: 我就问了 自带的不好用不快速吗~!
----------------------------------------------
哦哟喂,看过来: http://zelig.cn
作者:
男 dbyoung (dbyoung) ★☆☆☆☆ -
普通会员
2018/3/21 18:51:44
8楼: http://blog.csdn.net/dbyoung/article/details/7663259
----------------------------------------------
武汉天气不好
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/3/21 20:08:49
9楼: 6 楼的 可以考虑。
但是 高版本自带的也足够用了。
D7 用 indy 的 或  cnMd5 也足够了。
----------------------------------------------
(C)(P)Flying Wang
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/21 21:28:59
10楼: 除了楼上的还可以用blake2或者murmur算法
此帖子包含附件:xuchuantao_2018321212859.zip 大小:373.2K
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/21 21:31:37
11楼: 上面是blake2的代码,这个是murmur算法的代码,你要非常快这个可能是最好的了.
此帖子包含附件:xuchuantao_2018321213137.rar 大小:43.0K
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/21 21:37:04
12楼: 忘了还有这哥么HashLib4Pascal 上面两款比MD5快的算法发这哥么都有.
此帖子包含附件:xuchuantao_201832121373.rar 大小:1.25M
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/21 23:23:09
13楼: 刚才写了一个调用代码2.5G的文件用MurmurHash3前后30秒不到就计算完了.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/21 23:24:35
14楼: 不愧是Hash界的速度之王了
----------------------------------------------
按此在新窗口浏览图片
作者:
男 earthsbest (全能中间件) ▲▲▲▲△ -
普通会员
2018/3/22 8:56:36
15楼: 一般自带的够用了,性能最求极致的,可以考虑用第三方的。下面是System.Hash 和 FastMD5 的对比图
此帖子包含附件:
PNG 图像
大小:20.8K
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/22 15:42:37
16楼: 谢谢楼上各位,

15~30秒还是觉得有点慢...如果硬盘有几百千个 几G 的文件...

这样不但耗时,还把电脑资源耗光了...

我查到另类的方法,网上有人提过已做过,但无公开如何做...据说几秒内..

不知道有没有人做过? 提供下简单 代码 或 思路?

方法:
判断文件的 size 是否一样,,一样的话.
取 大文件的 10个不同位置..每个位置 取 10~100M 字节 的 二进制 码来判断 .

(如何取大文件的10个不同位置,每个位置取10 M字节 ,取到 字节后 转 字符串 写入 数据库,这样下次有相同文件就 取 特征码 对比就行: 这个如何写代码呢? 谢谢)
----------------------------------------------
谢谢各位大大
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/22 18:38:39
17楼: MurmurHash3 Hash 2.5G的文件15秒还慢?悄那个方法很容易被别人攻击的.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/22 18:39:56
18楼: 你Hash文件是用来比较一致性,还是用来加快上传速度的?
----------------------------------------------
按此在新窗口浏览图片
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/22 21:43:58
19楼: 谢谢楼上...
我本意想唯一性...但 找了很久,发现比较慢...15秒只是2g文件,现在很多6G以上的文件的. 如 iso 文件...或 视频文件.....估计要 40~60秒...如果有 几十个上百个 甚至上千...那 实在 速度和性能 都受不了...

所以现在改下方向...改为 判断 某几个位置的 二进制 码了...

我发现现在有些传输软件都是采用类似的技术的...

安全性?这个倒没有想过...会有什么问题吗?
----------------------------------------------
谢谢各位大大
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/22 22:13:02
20楼: uses HlpHashFactory;
THashFactory.THash128.CreateMurmurHash3_x86_128.ComputeFile
    (FileName).ToString()
2.5G文件 8.29秒你没测试伙介绍你的东西.
当然伙也没完全测试
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/22 22:25:54
21楼: 你要速度快可以用分块来Hash文件的方法,Hash大文件之所以慢是因为文件越大碰撞概率就越大越不容易实现一致性,相对的要实现一致性需要计算的次数就越多。
你可以试试分块传输,分块Hash,或者传输文件和Hash文件一起进行.
----------------------------------------------
按此在新窗口浏览图片
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/22 22:35:25
22楼: 你考虑的是那个时候的Hash性能?
----------------------------------------------
按此在新窗口浏览图片
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/22 23:23:25
23楼: 3q 楼上

其实如果一个6g以上文件...无论做什么都会慢的..

最快还是 分块.我把一个大文件分 100M 一块...我 只取 其中  10块 来 hash...

那样就会很快...

听那些做过的人说..几乎很少遇到一样了...
但他们不公开技术,而且不是delphi,告诉我也不会...

我是想这样处理的:
1:size 一样才 做 分块 hash..
2:如果 10块都一样..
3:才全文件 hash..

所以我现在只要 解决 如何取 分块 就 解决问题了...

(如何取大文件的10个不同位置,每个位置取 10 M 字节 ,取到 字节后 转 字符串 写入 数据库,这样下次有相同文件就 取 特征码 对比就行: 这个如何写代码呢? 谢谢)
----------------------------------------------
谢谢各位大大
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2018/3/23 0:25:54
24楼: size 一样才 做 分块 hash?那这个size存在哪里呢?去跟哪里的数据进行比较?

话说碰撞你们真的碰到过吗?碰撞的数据可能迄今为止也只有几十条吧?我觉得不用考虑,或者把这几十条数据直接写在代码里进行排除。
----------------------------------------------
只有偏执狂才能生存!
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/23 0:40:37
25楼: size 就是 文件的 大小...
上传 或 进行文件 比较 都是我的软件...故可以获取 文件的所有 参数...

1:先取文件大小...和数据库一样的..
2:就取 该 文件的 特征码,如 分割 1000块...取其中10块 做 md5,或直接将字节转 字符串...然后再对比 数据库 一样大小的 记录 的 特征码..
3:特征码都一样...就 整个文件进行 md5 对比..

现在我的问题是: 不会 第二步的 代码 如何写....

继续等......谢谢楼上各位 不吝赐教....
----------------------------------------------
谢谢各位大大
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/3/23 9:26:23
26楼: 楼主啊 流都不会用吗?
----------------------------------------------
(C)(P)Flying Wang
作者:
男 hsj (hsj) ★☆☆☆☆ -
盒子活跃会员
2018/3/23 10:27:24
27楼: 对速度有要求的可以用AdvApi32.dll来实现md5, 这个是微软的,应该比他快的不多了
----------------------------------------------
qq:171833017,靖源软件http://www.dxmylove.com
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/23 14:31:55
28楼: 流的方法我会研究下..谢谢
----------------------------------------------
谢谢各位大大
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/23 14:33:10
29楼: 谢谢楼上各位 不吝赐教....

我继续研究 流..然后 最后再用 xuchuantao (暗黑天使) 介绍的 和 fastmd5 试试.
AdvApi32.dll + 内存映射方式 也试试

不经过大家的意见..我是不知道有那么多方法可以处理的...再次感谢...
----------------------------------------------
谢谢各位大大
作者:
男 pankangkang (aaaa) ★☆☆☆☆ -
普通会员
2018/3/23 17:01:59
30楼: 如果从算法上优化  仅仅是从60秒 优化到50秒 这样的优化我认为其实没什么意义
从一个大文件 取10段流做特征码 无法保证特征码的准确性
如果速度上没有质的突破 建议从体验入手
一个超大的文件 不管是哪个网盘逻辑上一定是 先保证上传 异步验证 验证通过 再停止上传即可。

不用太纠结速度,普通文件fileMD5肯定已经够用
----------------------------------------------
-
作者:
男 earthsbest (全能中间件) ▲▲▲▲△ -
普通会员
2018/3/23 18:46:05
31楼: 内存映射最大貌似最大只支持 2G 文件?
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
作者:
男 gmxyb (gmxyb) ★☆☆☆☆ -
普通会员
2018/3/23 19:51:05
32楼: 对于超大的文件,每个都去计算全文hash效率确实太低了,算法再快也没用,瓶颈是在完整读一遍文件上。

我觉得楼主的思路挺好,特别是对于绝大部分文件都不会重复的场景,会很合适。
因为这里只是要判断两个文件内容“不相同”即可,相当于是用排除法。

我之前搞过类似的东西,用的是就是这个思路:

先按文件size比较,size不同,再看文件大小是否小于某个值(如32M),小于的直接取全文MD5比较,
超过的就从 头、中、尾 各取 1M 字节,计算一个MD5,如果还相同,就取全文MD5比较。

楼主取 10 段,我觉得有点夸张了。。
----------------------------------------------
-
作者:
男 gmxyb (gmxyb) ★☆☆☆☆ -
普通会员
2018/3/23 19:53:42
33楼: 至于说如何读取文件,这个用 TFileStream 就行了,用 Position 定位,Read 读取。。。delphi的基础知识。
----------------------------------------------
-
作者:
男 earthsbest (全能中间件) ▲▲▲▲△ -
普通会员
2018/3/24 10:29:58
34楼: 如果要做的比较严谨,全文Hash还是必不可少的,用来校验文件是否正确。
否则判断文件名算了,要什么自行车。
----------------------------------------------
Delphi4Linux Delphi三层/FireDAC 技术群:734515869 http://www.cnblogs.com/rtcmw
作者:
男 jackalan (nVicen) ★☆☆☆☆ -
盒子活跃会员
2018/3/24 12:43:02
35楼: 把文件拆100MB每块,随机取10块比较,相同再随机未做过的10块,原则上不同的文件速度会提高,相同的文件就是全文HASH,效率一样不高。
----------------------------------------------
简单做人,认真做事。
作者:
男 edwinyeah (Edwin) ★☆☆☆☆ -
盒子活跃会员
2018/3/24 13:45:28
36楼: 国外极负盛名的mORMot引擎里面的SynCrypto.pas里面有一个HashFile()函数,定义如下:
function HashFile(const aFileName: TFileName; aAlgo: THashAlgo): RawUTF8; overload;

其中 aAlgo表示计算哈希值要用的算法,支持以下7种算法:
MD5, 
SHA1, 
SHA256, 
SHA384, 
SHA512, 
SHA3_256, 
SHA3_512

详见:https://github.com/synopse/mORMot/blob/master/SynCrypto.pas#L2098

按照mORMot引擎的质量,效率肯定高,不过比起其它的具体如何就不清楚,楼主可以自己试试。
----------------------------------------------
-
作者:
男 moom (小新啊) ▲▲▲△△ -
普通会员
2018/3/24 14:33:09
37楼: 谢谢楼上...

试过了,一般..

MD5/sha1:  600 M .. 6.2s  ;  6G .. 62s
----------------------------------------------
谢谢各位大大
作者:
男 kentty (kentty) ★☆☆☆☆ -
普通会员
2018/3/24 17:11:59
38楼: lz的目的只是为了判断两个文件是不是相同,
第一步可以根据文件大小,如果大小都不一样,直接跳出,上传新文件
第二步根据文件大小在固定比例的几个位置(这几个相对位置不用公开)取一段数据做hash,把原文件几段hash结果存数据库,并和新文件对比,如果几段都相同,就认为是相同的,不上传新文件;如果不同,把新文件的几段hash也存数据库,并上传新文件

取多少段,每段取多长进行hash,就看lz期望的可靠度了。通常情况下除非是恶意攻击,两个文件大小相同、几个秘密位置段的hash相同,整个文件又不全同的概率几乎可以忽略
----------------------------------------------
-
作者:
男 xuchuantao (暗黑天使) ★☆☆☆☆ -
普通会员
2018/3/24 19:30:42
39楼: TCP是按块传输的,Hash算法也是按块计算的.对每个块进行Hash把计算量平躺在传输过程的整个时间轴上.这样是比较好的了.要比较一致性只要对Hash值进行二次Hash,然后比较就可以了
----------------------------------------------
按此在新窗口浏览图片
作者:
男 hexi (Hexi) ★☆☆☆☆ -
盒子活跃会员
2018/3/24 19:55:22
40楼: 应该计算两个时间:文件读取时间,Hash时间。
----------------------------------------------
-
作者:
男 edwinyeah (Edwin) ★☆☆☆☆ -
盒子活跃会员
2018/3/25 11:50:07
41楼: 我觉得对大文件要用内存映射文件,这是公认的提速方法。
----------------------------------------------
-
作者:
男 kenlewis (肥牛) ★☆☆☆☆ -
普通会员
2018/3/29 13:50:25
42楼: 我觉得你没必要对整个文件HASH。我提一个思路。
取文件大小,做一个值A。
把文件按字节分成N份,每份取第一个字节,B1……BN。
然后把A、B1……BN这个序列做HASH就可以了。速度应该能提升很多倍
----------------------------------------------
-
作者:
男 nevergrief (孤独骑士) ★☆☆☆☆ -
盒子活跃会员
2018/3/29 18:52:29
43楼: 楼上真绝,每个模块取1M的数据都是没有必要,只需要取一个字节,再拼凑一下就行,数据量非常小,然而却是几乎没有可能重叠。
----------------------------------------------
只有偏执狂才能生存!
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行117.1875毫秒 RSS