问题描述
我在写一个应用程序,应该将一堆文件从一个地方复制到另一个地方。
当我使用TFileStream的副本,它比用操作系统复制文件慢3-4倍。
我也试图用
我在Win32下工作,任何人都对这个问题有一些见解。
- 您可以调用CopyFile,使用
Windows API
- 您可以调用浏览器使用的api(windows api
)。
调用该函数的示例可以在
上找到 - 您可以编写自己的使用缓冲区的函数。
- 您可以调用浏览器使用的api(windows api
如果您知道要复制的文件类型,第3种方法通常会优于其他方法。因为Windows API更加适应整体最佳情况(小文件,大文件,网络上的文件,缓慢的驱动器上的文件)。
下面是我自己的缓冲复制函数(我已经删除了GUI回调):
过程CustomFileCopy(const ASourceFileName,ADestinationFileName:TFileName);
const
BufferSize = 1024; // 1KB blocks,change this to tune your speed
var
缓冲区:Byte的数组;
ASourceFile,ADestinationFile:THandle;
FileSize:DWORD;
BytesRead,BytesWritten,BytesWritten2:DWORD;
begin
SetLength(Buffer,BufferSize);
ASourceFile:= OpenLongFileName(ASourceFileName,0);
如果ASourceFile<> 0 then
try
FileSize:= FileSeek(ASourceFile,0,FILE_END);
FileSeek(ASourceFile,0,FILE_BEGIN);
ADestinationFile:= CreateLongFileName(ADestinationFileName,FILE_SHARE_READ);
if ADestinationFile<> 0,则
try
while(FileSize-FileSeek(ASourceFile,0,FILE_CURRENT))> = BufferSize do
begin
if(not ReadFile(ASourceFile,Buffer [0] BufferSize,BytesRead,nil))和(BytesRead = 0)then
Continue;
WriteFile(ADestinationFile,Buffer [0],BytesRead,BytesWritten,nil);
if ByteWritten< BytesRead then
begin
WriteFile(ADestinationFile,Buffer [BytesWritten],BytesRead - BytesWritten,BytesWritten2,nil);
if(BytesWritten2 + BytesWritten) BytesRead then
RaiseLastOSError;
end;
end;
if FileSeek(ASourceFile,0,FILE_CURRENT)< FileSize then
begin
if(not ReadFile(ASourceFile,Buffer [0],FileSize - FileSeek(ASourceFile,0,FILE_CURRENT),BytesRead,nil))和(BytesRead = 0)then
ReadFile(ASourceFile,Buffer [0],FileSize - FileSeek(ASourceFile,0,FILE_CURRENT),BytesRead,nil);
WriteFile(ADestinationFile,Buffer [0],BytesRead,BytesWritten,nil);
if ByteWritten< BytesRead then
begin
WriteFile(ADestinationFile,Buffer [BytesWritten],BytesRead - BytesWritten,BytesWritten2,nil);
if(BytesWritten2 + BytesWritten) BytesRead then
RaiseLastOSError;
end;
end;
finally
CloseHandle(ADestinationFile);
end;
finally
CloseHandle(ASourceFile);
end;
end;
自己的功能:
function OpenLongFileName(const ALongFileName:String; SharingMode:DWORD):THandle;超载;
begin
如果CompareMem(@(ALongFileName [1]),@('\\'[1]),2)then
{Allready an UNC path}
结果:= CreateFileW(PWideChar(WideString(ALongFileName)),GENERIC_READ,SharingMode,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
else
结果:= CreateFileW(WideString('\\? \\'+ ALongFileName)),GENERIC_READ,SharingMode,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
end;
function OpenLongFileName(const ALongFileName:WideString; SharingMode:DWORD):THandle;超载;
begin
if CompareMem(@(WideCharToString(PWideChar(ALongFileName))[1]),@('\\'[1]),2)then
{Allready an UNC路径}
结果:= CreateFileW(PWideChar(ALongFileName),GENERIC_READ,SharingMode,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
else
结果:= CreateFileW('\\? \'+ ALongFileName),GENERIC_READ,SharingMode,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
end;
函数CreateLongFileName(const ALongFileName:String; SharingMode:DWORD):THandle;超载;
begin
如果CompareMem(@(ALongFileName [1]),@('\\'[1]),2)then
{Allready an UNC path}
结果:= CreateFileW(PWideChar(WideString(ALongFileName)),GENERIC_WRITE,SharingMode,nil,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
else
结果:= CreateFileW(WideString('\\? \\'+ ALongFileName)),GENERIC_WRITE,SharingMode,nil,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
end;
function CreateLongFileName(const ALongFileName:WideString; SharingMode:DWORD):THandle;超载;
begin
if CompareMem(@(WideCharToString(PWideChar(ALongFileName))[1]),@('\\'[1]),2)then
{Allready an UNC路径}
结果:= CreateFileW(PWideChar(ALongFileName),GENERIC_WRITE,SharingMode,nil,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)
else
结果:= CreateFileW('\\? \'+ ALongFileName),GENERIC_WRITE,SharingMode,nil,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
end;
代码需要更长的时间,因为我包括一个重试机制来支持WiFi连接问题
如果BytesWritten< BytesRead then
begin
WriteFile(ADestinationFile,Buffer [BytesWritten],BytesRead - BytesWritten,BytesWritten2,nil);
if(BytesWritten2 + BytesWritten) BytesRead then
RaiseLastOSError;
end;
可以写为
if ByteWritten< BytesRead then
begin
RaiseLastOSError;
end;
I'm writing an app that supposed to copy a bunch of files from one place to another.When I'm using TFileStream for the copy it is 3-4 times slower than copying the files with the OS.
I also tried to copy with a buffer, but that was too slow aswell.
I'm working under Win32, anyone got some insights on this matter?
There are a few options.
- You could call CopyFile which usesthe CopyFileA windows API
- You could call the api which explorer uses (the windows apiSHFileOperation). An example ofcalling that function can be found onSCIP.be
- You could write your own function which uses a buffer.
If you know the kind of files your going to copy, the 3th method will normally outperform the others. Because the windows API's are more tuned for overall best case (small files, large files, files over network, files on slow drives). You can tune your own copy function more to fit your needs.
Below is my own buffered copy function (i've stripped out the GUI callbacks):
procedure CustomFileCopy(const ASourceFileName, ADestinationFileName: TFileName);
const
BufferSize = 1024; // 1KB blocks, change this to tune your speed
var
Buffer : array of Byte;
ASourceFile, ADestinationFile: THandle;
FileSize: DWORD;
BytesRead, BytesWritten, BytesWritten2: DWORD;
begin
SetLength(Buffer, BufferSize);
ASourceFile := OpenLongFileName(ASourceFileName, 0);
if ASourceFile <> 0 then
try
FileSize := FileSeek(ASourceFile, 0, FILE_END);
FileSeek(ASourceFile, 0, FILE_BEGIN);
ADestinationFile := CreateLongFileName(ADestinationFileName, FILE_SHARE_READ);
if ADestinationFile <> 0 then
try
while (FileSize - FileSeek(ASourceFile, 0, FILE_CURRENT)) >= BufferSize do
begin
if (not ReadFile(ASourceFile, Buffer[0], BufferSize, BytesRead, nil)) and (BytesRead = 0) then
Continue;
WriteFile(ADestinationFile, Buffer[0], BytesRead, BytesWritten, nil);
if BytesWritten < BytesRead then
begin
WriteFile(ADestinationFile, Buffer[BytesWritten], BytesRead - BytesWritten, BytesWritten2, nil);
if (BytesWritten2 + BytesWritten) < BytesRead then
RaiseLastOSError;
end;
end;
if FileSeek(ASourceFile, 0, FILE_CURRENT) < FileSize then
begin
if (not ReadFile(ASourceFile, Buffer[0], FileSize - FileSeek(ASourceFile, 0, FILE_CURRENT), BytesRead, nil)) and (BytesRead = 0) then
ReadFile(ASourceFile, Buffer[0], FileSize - FileSeek(ASourceFile, 0, FILE_CURRENT), BytesRead, nil);
WriteFile(ADestinationFile, Buffer[0], BytesRead, BytesWritten, nil);
if BytesWritten < BytesRead then
begin
WriteFile(ADestinationFile, Buffer[BytesWritten], BytesRead - BytesWritten, BytesWritten2, nil);
if (BytesWritten2 + BytesWritten) < BytesRead then
RaiseLastOSError;
end;
end;
finally
CloseHandle(ADestinationFile);
end;
finally
CloseHandle(ASourceFile);
end;
end;
Own functions:
function OpenLongFileName(const ALongFileName: String; SharingMode: DWORD): THandle; overload;
begin
if CompareMem(@(ALongFileName[1]), @('\\'[1]), 2) then
{ Allready an UNC path }
Result := CreateFileW(PWideChar(WideString(ALongFileName)), GENERIC_READ, SharingMode, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
else
Result := CreateFileW(PWideChar(WideString('\\?\' + ALongFileName)), GENERIC_READ, SharingMode, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
end;
function OpenLongFileName(const ALongFileName: WideString; SharingMode: DWORD): THandle; overload;
begin
if CompareMem(@(WideCharToString(PWideChar(ALongFileName))[1]), @('\\'[1]), 2) then
{ Allready an UNC path }
Result := CreateFileW(PWideChar(ALongFileName), GENERIC_READ, SharingMode, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
else
Result := CreateFileW(PWideChar('\\?\' + ALongFileName), GENERIC_READ, SharingMode, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
end;
function CreateLongFileName(const ALongFileName: String; SharingMode: DWORD): THandle; overload;
begin
if CompareMem(@(ALongFileName[1]), @('\\'[1]), 2) then
{ Allready an UNC path }
Result := CreateFileW(PWideChar(WideString(ALongFileName)), GENERIC_WRITE, SharingMode, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
else
Result := CreateFileW(PWideChar(WideString('\\?\' + ALongFileName)), GENERIC_WRITE, SharingMode, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
end;
function CreateLongFileName(const ALongFileName: WideString; SharingMode: DWORD): THandle; overload;
begin
if CompareMem(@(WideCharToString(PWideChar(ALongFileName))[1]), @('\\'[1]), 2) then
{ Allready an UNC path }
Result := CreateFileW(PWideChar(ALongFileName), GENERIC_WRITE, SharingMode, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
else
Result := CreateFileW(PWideChar('\\?\' + ALongFileName), GENERIC_WRITE, SharingMode, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
end;
The code is a bit longer that necessary, because I included a retry mechanism to support a wifi connection problem I had.
So this part
if BytesWritten < BytesRead then
begin
WriteFile(ADestinationFile, Buffer[BytesWritten], BytesRead - BytesWritten, BytesWritten2, nil);
if (BytesWritten2 + BytesWritten) < BytesRead then
RaiseLastOSError;
end;
could be written as
if BytesWritten < BytesRead then
begin
RaiseLastOSError;
end;
这篇关于Delphi快速文件复制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!