Delphi快速文件复制 [英] Delphi fast file copy

查看:174
本文介绍了Delphi快速文件复制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在写一个应用程序,应该将一堆文件从一个地方复制到另一个地方。
当我使用TFileStream的副本,它比用操作系统复制文件慢3-4倍。



我也试图用



我在Win32下工作,任何人都对这个问题有一些见解。


  1. 您可以调用CopyFile,使用
    CopyFileA Windows API


    • 您可以调用浏览器使用的api(windows api
      SHFileOperation )。
      调用该函数的示例可以在
      上找到 SCIP.be

    • 您可以编写自己的使用缓冲区的函数。


如果您知道要复制的文件类型,第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.

  1. You could call CopyFile which uses the CopyFileA windows API
    • You could call the api which explorer uses (the windows api SHFileOperation). An example of calling that function can be found on SCIP.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快速文件复制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆