-1

全局目标是使用文件的一部分来获取校验和以查找重复的电影和 mp3 文件,为此我必须获取文件的一部分并生成 md5,因为在某些情况下整个文件大小高达 25 gigs,如果我找到重复项然后我将做一个完整的 md5 以避免错误删除文件的任何错误我没有任何问题我从流中生成 md5 它将使用 indy 组件完成所以对于第一部分我必须复制文件的前 1mb

所以我做了这个功能

但是所有检查的内存流都是空的!

function splitFile(FileName: string): TMemoryStream;
 var
    fs: TFileStream;
    ms : TMemoryStream;
 begin
     fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite) ;
     ms := TMemoryStream.Create;
     fs.Position :=0;
     ms.CopyFrom(fs, 1048576);
     result := ms;
 end;

我怎样才能解决这个问题?或者我的问题在哪里?

update1 - (肮脏的测试):

此代码返回错误stream read error也 memo2 显示一些字符串,但 memo3 为空!

function splitFile(FileName: string): TMemoryStream;
 var
    fs: TFileStream;
    ms : TMemoryStream;
 begin
     fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite) ;
     ms := TMemoryStream.Create;
     fs.Position :=0;
      form1.Memo2.Lines.LoadFromStream(fs);
     ms.CopyFrom(fs,1048576);
     ms.Position := 0;
      form1.Memo3.Lines.LoadFromStream(ms);
     result := ms;
 end;

完整的代码

function splitFile(FileName: string): TMemoryStream;
 var
    fs: TFileStream;
    ms : TMemoryStream;
    i,BytesToRead : integer;
 begin

     fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
     ms := TMemoryStream.Create;
     fs.Position :=0;
     BytesToRead := Min(fs.Size-fs.Position, 1024*1024);
      ms.CopyFrom(fs, BytesToRead);
     result := ms;
    // fs.Free;
    // ms.Free;
 end;

function streamFile(FileName: string): TFileStream;
 var
    fs: TFileStream;
    ms : TMemoryStream;
 begin
     fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite) ;
     result := fs;
 end;

 function GetFileMD5(const Stream: TStream): String; overload;
var MD5: TIdHashMessageDigest5;
begin
    MD5 := TIdHashMessageDigest5.Create;
    try
       Result := MD5.HashStreamAsHex(Stream);
    finally
       MD5.Free;
    end;
end;

function getMd5HashString(value: string): string;
var
    hashMessageDigest5 : TIdHashMessageDigest5;
begin
    hashMessageDigest5 := nil;
    try
        hashMessageDigest5 := TIdHashMessageDigest5.Create;
        Result := IdGlobal.IndyLowerCase ( hashMessageDigest5.HashStringAsHex ( value ) );
    finally
        hashMessageDigest5.Free;
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Path,hash    : String;
  SR      : TSearchRec;
begin
   if od1.Execute then
  begin
    Path:=ExtractFileDir(od1.FileName); //Get the path of the selected file
    DirList:=TStringList.Create;
    try
          if FindFirst(Path+'\*.*', faArchive  , SR) = 0 then
          begin
            repeat
              if (SR.Size>10240) then
              begin
                hash := GetFileMD5(splitFile(Path+'\'+SR.Name));
              end
              else
              begin
                hash := GetFileMD5(streamFile(Path+'\'+SR.Name));
              end;
                memo1.Lines.Add(hash+' | '+SR.Name +' | '+inttostr(SR.Size));
                application.ProcessMessages;
            until FindNext(SR) <> 0;
            FindClose(SR);
          end;
   finally
     DirList.Free;
   end;
  end;
end;

输出:

D41D8CD98F00B204E9800998ECF8427E | eslahat.docx | 13338
D41D8CD98F00B204E9800998ECF8427E | EXT-3000-Data-Sheet.pdf | 682242
D41D8CD98F00B204E9800998ECF8427E | faktor khate ekhtesasi firoozpoor.pdf | 50091
D41D8CD98F00B204E9800998ECF8427E | FileZilla_3.9.0.5_win32-setup.exe | 6057862
D41D8CD98F00B204E9800998ECF8427E | FileZilla_3.9.0.6_win32-setup.exe | 6126536
11210486C9E54E12DA9DF687792257EA | get_stats_of_all_members_of_mu(1).php | 6227
11210486C9E54E12DA9DF687792257EA | get_stats_of_all_members_of_mu.php | 6227
D41D8CD98F00B204E9800998ECF8427E | GOMAUDIOGLOBALSETUP.EXE | 6855616
D41D8CD98F00B204E9800998ECF8427E | harvester-master(1).zip | 54255
D41D8CD98F00B204E9800998ECF8427E | harvester-master.zip | 54180
4

2 回答 2

4

这是我快速为您编写的一个程序,它可以让您将部分文件(块)读入内存流。

我把它变成一个过程而不是函数的原因是可以为不同的块重用相同的内存流。这样您就可以避免所有这些内存分配/解除分配,并减少引入内存泄漏的机会。

为了能够做到这一点,您需要将内存流句柄作为变量参数传递给过程。

我还添加了另外两个参数。一种用于指定块大小(要从文件中读取的数据量)和块编号。

我还做了一些基本的保护措施来告诉你何时要读取超出文件范围的块。并且还能够自动减小最后一个块的大小,因为并非所有文件大小都是 oyur 块大小的倍数(在您的情况下,并非所有文件的大小都是 X 兆字节,其中 X 是任何有效的整数)。

procedure readFileChunk(FileName: string; var MS: TMemoryStream; ChunkNo: Integer; ChunkSize: Int64);
var fs: TFileStream;
begin
  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  if ChunkSize * (ChunkNo-1) <= fs.Size then
  begin
    fs.Position := ChunkSize * (ChunkNo-1);
    if fs.Position + ChunkSize <= fs.Size then
      ms.CopyFrom(fs, ChunkSize)
    else
      ms.CopyFrom(fs, fs.Size - fs.Position);
  end
  else
    MessageBox(Form2.WindowHandle, 'File does not have so many chunks', 'WARNING!', MB_OK);
  fs.Free;
end;

您可以通过调用来使用此过程:

readFileChunk(FileName,MemoryStream,ChunkNumber,ChunkSize);

在调用此过程之前,请确保您已经创建了内存流。
此外,如果您想多次重用相同的内存流,请不要忘记在调用此过程之前将其位置设置为 0,否则新数据将添加到流的末尾,进而不断增加内存流的大小。

更新:

经过一些试验后,我发现问题出在您的 GetFileMD5 方法中。

我无法确切解释为什么会发生这种情况,但是如果您将 TMemoryStream 传递给 TStream 参数,则 TStream 参数只是不接受它,因此 MD5 散列算法将其视为空句柄。
当我将参数类型更改为 TMemoryStream 时,代码可以正常工作,但您不再可以将 TFileStream 传递给 GetFileMD5 方法,因此它破坏了之前工作的整个文件的哈希生成。

解决方案:

所以在做了更多的挖掘之后,我有一个好消息要告诉你。

你甚至不需要使用 TMemoryStreams。“HashStreamAsHex”函数可以接受两个可选参数,它们允许您定义数据的起点和要从中生成 MD5 哈希字符串的数据块的大小。这也适用于 TFileStream。

因此,为了从文件的一小部分生成 MD5 哈希字符串,请调用以下命令:

MD5.HashStreamAsHex(Stream,StartPosition,DataSize);

StartPositon 为散列操作指定流中的初始偏移量。当 StartPosition 包含一个正的非零值时,流位置会在计算哈希值之前移动到指定的偏移量。当 StartPosition 包含值 -1 时,流的当前位置用作指定流的初始偏移量。

DataSize 指示流中要包含在散列操作中的字节数。当 DataSize 包含负值 (<0) 时,从当前流位置剩余的字节用于散列操作。否则,使用 DataSize 中的字节数。如果 DataSize 大于流的大小,则将两个值中较小的一个用于操作。

在您从第一个兆字节获取 MD5 哈希的情况下,您可以调用:

MD5.HashStreamAsHex(Stream,0,1024*1024);

现在我相信您可以修改其余代码以使其按您的意愿工作。如果不告诉它停在哪里,我会帮助你。

于 2014-11-22T01:47:02.577 回答
3

我假设您的代码不会引发异常。如果是这样,您肯定会提到这一点。我还假设该文件足够大以供您尝试读取。

您的代码确实复制了。如果调用CopyFrom没有引发异常,则内存流包含1024000文件的第一个字节。

但是,在调用 之后CopyFrom,内存流的指针位于流的末尾,因此如果您从中读取,您将无法读取任何内容。也许您需要将流指针移到开头:

ms.Position := 0;

然后从内存流中读取。

1MB = 1024*1024,FWIW。


更新

可能我上面的假设是不正确的。您的代码似乎可能引发异常,因为您尝试读取文件末尾之外的内容。

您真正想要做的是尽可能多地阅读文件的第一部分。那是一条两线。

BytesToRead := Min(Source.Size-Source.Position, 1024*1024);
Dest.CopyFrom(Source, BytesToRead);
于 2014-11-22T00:02:18.967 回答