全球目标是 使用文件的一部分来获取校验和以查找重复的电影和mp3文件, 为了这个目标,我必须得到文件的一部分并生成md5,因为在某些情况下整个文件大小高达25演出,如果我发现重复,那么我将做一个完整的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
答案 0 :(得分: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方法了,所以它破坏了之前工作的整个文件的哈希生成。
<强> SOLUTION:强>
所以在做了一些挖掘后,我有一个很棒的消息。
您甚至不需要使用TMemoryStreams。 “HashStreamAsHex”函数可以接受两个可选参数,这些参数允许您定义数据的起始点以及要从中生成MD5哈希字符串的数据块的大小。这也适用于TFileStream。
因此,为了从文件的一小部分生成MD5哈希字符串,请调用:
MD5.HashStreamAsHex(Stream,StartPosition,DataSize);
StartPositon指定用于散列操作的流的初始偏移量。当StartPosition包含正的非零值时,在计算哈希值之前将流位置移动到指定的偏移量。当StartPosition包含值-1时,流的当前位置将用作指定流的初始偏移量。
DataSize指示要包含在散列操作中的流的字节数。当DataSize包含负值(&lt; 0)时,从当前流位置剩余的字节用于散列操作。否则,使用DataSize中的字节数。如果DataSize大于流的大小,则两个值中较小的一个用于操作。
在您从第一个MegaByte获取MD5哈希的情况下,您可以调用:
MD5.HashStreamAsHex(Stream,0,1024*1024);
现在我相信你可以修改你的其余代码,让它按你的意愿运行。如果不知道它停在哪里,我会帮助你。
答案 1 :(得分:3)
我假设你的代码没有引发异常。如果确实如此,你肯定会提到这一点。我还假设文件足够大,可以尝试阅读。
您的代码会复制。如果对CopyFrom
的调用未引发异常,则内存流包含文件的第一个1024000
字节。
但是,在调用CopyFrom
之后,内存流的指针位于流的末尾,因此如果从中读取,则无法读取任何内容。也许您需要将流指针移动到开头:
ms.Position := 0;
然后从内存流中读取。
1MB = 1024 * 1024,FWIW。
<强>更新强>
可能我上面的假设是不正确的。您的代码似乎可能会引发异常,因为您尝试读取超出文件末尾的内容。
您真正想要做的是尽可能多地阅读文件的第一部分。这是一个双线。
BytesToRead := Min(Source.Size-Source.Position, 1024*1024);
Dest.CopyFrom(Source, BytesToRead);