views:

550

answers:

4

Hi

In my application I need to copy over 1000 small files

Here is the code I am using but it is VERY SLOW Is there a better way of doing this ?

procedure Tdatafeeds.RestotreTodaysFiles;
var
  SearchRec: TSearchRec;
  FromFn, ToFn: string;
Begin
    if DirectoryExists(BackupPath1) then
    begin
      try
        if FindFirst(BackupPath1 + '\*.*', (faAnyFile AND NOT(faDirectory)), SearchRec) = 0 then
        begin
          repeat
            FromFn := BackupPath1 + '\' + SearchRec.name;
            ToFn := DatafeedsPath1 + '\' + SearchRec.name;
            CopyFile(Pchar(FromFn), Pchar(ToFn), false);
          until FindNext(SearchRec) <> 0;
        end;
      finally
        FindClose(SearchRec);
      end;
    end;
End;
A: 

Perhaps you might experiment with reading a bunch of files into memory and then writing them all to disk at once (like XCOPY). That might be nicer on the filesystem.

Anthony Mills
+6  A: 

You can use the SHFileOperation() API call and use a wildcard in the file name of the struct. That way one call would be used to copy all of the files in one go. There's even the possibility to show the progress (via a callback function) and allow the user to cancel the operation.

mghie
CopyFileEx() will allow the callback too, and I'd wonder if SHFileOperation would not end calling CopyFileEx() too.
ldsandon
True, but AFAICS from the MSDN documentation `CopyFileEx()` will not allow wildcards for the source. So using `SHFileOperation()` (or the `IFileOperation` interface on Vista and beyond) would replace the file find loop as well.
mghie
Thanks - SHFileOperation() works well the copy now takes seconds - before it took minutes :)
Charles Faiga
+2  A: 

I can't test your code right now, but check out this corrected version

    // (!) faAnyFile-faDirectory <--- this is wrong
    // we don't subtract flag values because the value will be meaningless
    if FindFirst(BackupPath1 + '\*.*', faAnyFile, SearchRec) = 0 then
    begin
      repeat
        if  not (SearchRec.Attr and faDirectory)
            And SearchRec.Name <> "."
            And SearchRec.Name <> ".." Then
        Begin
          FromFn := BackupPath1 + '\' + SearchRec.name;
          ToFn := DatafeedsPath1 + '\' + SearchRec.name;
          CopyFile(Pchar(FromFn), Pchar(ToFn), false);
        End;
      until FindNext(SearchRec) <> 0;
      FindClose(SearchRec);
    end;
Nick D
+6  A: 

Definitely go with SHFileOperation() as suggested above, CopyFile is way too slow for that many files. It looks like you are basically restoring an entire folder so the search function may be unnecessary and slow things down further. Something like this may be of help:

uses ShellApi;

function CopyDir(const fromDir, toDir: string): Boolean;
var
  fos: TSHFileOpStruct;
begin
  ZeroMemory(@fos, SizeOf(fos));
  with fos do
  begin
    wFunc  := FO_COPY;
    fFlags := FOF_FILESONLY;
    pFrom  := PChar(fromDir + #0);
    pTo    := PChar(toDir)
  end;
  Result := (0 = ShFileOperation(fos));
end;

This function will raise a prompt to overwrite existing files though (maybe it can be tweaked to skip that) but the user can select "All" so it's a one-click procedure, much faster, has a progress bar and can be canceled if desired.

EagleOfToledo
Have a look at the **FOF_NOCONFIRMATION** and **FOF_NOCONFIRMMKDIR** flags to avoid any prompts. Reference: http://msdn.microsoft.com/en-us/library/bb759795%28VS.85%29.aspx
stukelly
What makes SHFileOperation() faster than CopyFile?
Lars D