Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
-Delphi/Pascal
-CBuilder/C++
-C#Builder/C#
-JBuilder/Java
-Kylix
Member Area
-Home
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Login/Logout
-Become a Member
-Why sign up!
-Newsletter
-Chat Online!
-Indexes NEW!!
Employment
-Build your resume
-Find a job
-Post a job
-Resume Search
Contacts
-Contacts
-Feedbacks
-Link to us
-Privacy/Disclaimer
Embarcadero
Visit Embarcadero
Embarcadero Community
JEDI
Links
How to copy Files in Delphi Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
19-Nov-02
Category
Files Operation
Language
Delphi All Versions
Views
159
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Lou Adler

How do I copy a file in Delphi?

Answer:

Reminiscing on Days Gone By...

Back in the old DOS days, we took for granted copying a file from one place to 
another with the copy command. But with Windows, everything changed. We now use 
File Manager or Explorer to copy files from one place to another, which is a huge 
improvement over typing in the fully qualified path for both source and destination 
files.

But at the programming level, performing the copying of one file to another is not 
as apparent as one would think. In fact, there are no native Delphi calls for 
copying a file whatsoever. So what do you do if you want to copy a file? You have 
to write the routine yourself.

Interestingly enough, there is a pretty good example of copying a file that is in 
the FMXUTILS.PAS file in the Delphi\Demos\Doc\Filmanex directory that will perform 
a file copy using native Delphi file-related commands. While this method works just 
fine, I decided to go another route; that is, to use a file stream to copy a file 
from one place to another. Streams are interesting animals. They're used internally 
in Delphi to read and write components, forms and data, and they're pretty handy. 
Unfortunately, they aren't well-documented so they can be a bit tricky to use. I 
went through a lot of trial and error to get them to work, and referenced several 
sources outside of the online help (which is just about the only place you'll find 
anything on streams in Delphi) before I got a handle on streams. But once I figured 
them out, they became what I use for reading and writing files almost exclusively.

There's Almost Always More Than One Way of Doing Things...

Once you've programmed for a while, you realize that it's possible to solve a 
particular problem in a variety of ways; which way is valid is dependent upon your 
knowledge and experience (one way may be more optimized than another) or, at times, 
even the situation will dictate that one methodology is better suited for a task 
than another.

For instance, with file copying, there are times you just want to copy a file in 
the background using a quick and dirty method, and you don't care if the user knows 
what's going on at all. But there are other times, such as when file utilities are 
part of an interface, when you want the user to be aware of the copying progress.

What I'm going to show you here are two ways to perform file copying: one quick and 
dirty; the other, a more snazzy, graphical way of copying a file, though it uses a 
few more resources and is a bit slower.

Quick and Dirty Copying

Traditionally, copying a file involves using a loop to move a series of blocks from 
one file into a temporary buffer, then copying the contents of the buffer into 
another file. Let's look at the CopyFile function found in the FMXUTILS.PAS:
1   
2   {=============================================================================
3    CopyFile procedure found in the FMXUTILS.PAS file in Delphi\Demos\Doc\Filmanex
4    This is an example of copying a file using a buffer.
5    =============================================================================}
6   
7   procedure CopyFile(const FileName, DestName: TFileName);
8   var
9     CopyBuffer: Pointer; { buffer for copying }
10    TimeStamp, BytesCopied: Longint;
11    Source, Dest: Integer; { handles }
12    Destination: TFileName; { holder for expanded destination name }
13  const
14    ChunkSize: Longint = 8192; { copy in 8K chunks }
15  begin
16    Destination := ExpandFileName(DestName); { expand the destination path }
17    if HasAttr(Destination, faDirectory) then { if destination is a directory... }
18      Destination := Destination + '\' + ExtractFileName(FileName);
19        { ...clone file name }
20    TimeStamp := FileAge(FileName); { get source's time stamp }
21    GetMem(CopyBuffer, ChunkSize); { allocate the buffer }
22    try
23      Source := FileOpen(FileName, fmShareDenyWrite); { open source file }
24      if Source < 0 then
25        raise EFOpenError.Create(FmtLoadStr(SFOpenError, [FileName]));
26      try
27        Dest := FileCreate(Destination); { create output file; overwrite existing }
28        if Dest < 0 then
29          raise EFCreateError.Create(FmtLoadStr(SFCreateError, [Destination]));
30        try
31          repeat
32            BytesCopied := FileRead(Source, CopyBuffer^, ChunkSize); { read chunk }
33            if BytesCopied > 0 then { if we read anything... }
34              FileWrite(Dest, CopyBuffer^, BytesCopied); { ...write chunk }
35          until BytesCopied < ChunkSize; { until we run out of chunks }
36        finally
37          FileClose(Dest); { close the destination file }
38        end;
39      finally
40        FileClose(Source); { close the source file }
41      end;
42    finally
43      FreeMem(CopyBuffer, ChunkSize); { free the buffer }
44    end;
45  end;


But Delphi implements a method of TStream called CopyFrom that allows you to copy 
the entire contents of one stream into another in one fell swoop. Here's an 
implementation of copying a file using the CopyFrom method:
46  
47  {=============================================================
48   Quick and dirty copying using the CopyFrom method of TStream.
49   =============================================================}
50  
51  procedure FileCopy(const FSrc, FDst: string);
52  var
53    sStream,
54      dStream: TFileStream;
55  begin
56    sStream := TFileStream.Create(FSrc, fmOpenRead);
57    try
58      dStream := TFileStream.Create(FDst, fmCreate);
59      try
60        {Forget about block reads and writes, just copy
61         the whole darn thing.}
62        dStream.CopyFrom(sStream, 0);
63      finally
64        dStream.Free;
65      end;
66    finally
67      sStream.Free;
68    end;
69  end;


The declaration of the CopyFrom method is as follows:

function CopyFrom(Source: TStream; Count: LongInt): LongInt;

Source is the TStream you're going to copy from, and Count is the number of bytes 
to copy from the stream. If Count is zero (0), the entire contents of the source 
stream is copied over. This makes for a quick one-liner copying.

Notice that in both the examples above, all the functionality is enclosed in nested 
try..finally blocks. This is extremely important because just in case something 
goes wrong, all resources and pointers that are created are freed. You don't want 
to have stray pointers or unreleased memory in your system, so providing at least 
this level of exception handling is key to ensuring that you don't.

A Sexier File Copy

If you write robust user interfaces, practically everything that you do involves 
interacting with the user by providing visual cues to let the user know what's 
going on. File copying is one of those types of operations that when performed 
within the context of a user interface must provide some status as to the progress 
of the copy operation. Therefore, a quick and dirty copy like the one I just 
described above won't do. What we need then is something with a bit more pizazz.

In order to get status, we need to copy the file in chunks. That way, as we copy 
each chunk from one file to another, we can let the user know how far we've 
proceeded. What this implies is that we need two pieces. The first is the unit that 
performs the copying; the other a status window used for notification. For me, the 
best way to get both pieces to work in concert was to build a custom component 
which encapsulates the file copy operation and uses another unit to perform the 
notification.

The notification unit is just a simple form with a TGauge and a TButton placed on 
it. The unit code is as follows:

70  unit copyprg;
71  
72  interface
73  
74  uses
75    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
76    StdCtrls, Gauges;
77  
78  type
79    TFileProg = class(TForm)
80      Gauge1: TGauge;
81      Button1: TButton;
82      procedure Button1Click(Sender: TObject);
83      procedure FormCreate(Sender: TObject);
84    private
85      { Private declarations }
86      fCancel: Boolean;
87    public
88      property CancelIt: Boolean read fCancel;
89    end;
90  
91  var
92    FileProg: TFileProg;
93  
94  implementation
95  
96  {$R *.DFM}
97  
98  procedure TFileProg.Button1Click(Sender: TObject);
99  begin
100   fCancel := True;
101 end;
102 
103 procedure TFileProg.FormCreate(Sender: TObject);
104 begin
105   fCancel := False;
106 end;
107 
108 end.


Nothing odd here. I simply added a custom property to the form called CancelIt, 
which is a simple Boolean flag used to cancel the copying operation midstream 
should the user desire to do so. The real work happens in the custom component 
itself. Let's look at its code, then discuss it:

109 unit FileCopy;
110 
111 interface
112 
113 uses
114   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
115 
116 type
117   TFileCopy = class(TComponent)
118   private
119     FSource,
120       FDest: string;
121     procedure DoCopyFile(const SrcFile, DstFile: string);
122   public
123     procedure CopyFile; virtual;
124   published
125     property FileSource: string read FSource write FSource;
126     property FileDestination: string read FDest write FDest;
127   end;
128 
129 procedure register;
130 
131 implementation
132 
133 uses copyprg;
134 
135 procedure TFileCopy.CopyFile;
136 begin
137   DoCopyFile(FileSource, FileDestination);
138 end;
139 
140 procedure TFileCopy.DoCopyFile(const SrcFile, DstFile: string);
141 const
142   bufSize = 16384; {Use a 16K buffer. You can use whatever size suits you, though.}
143 var
144   sStream,
145     dStream: TFileStream;
146   pBuf: Pointer;
147   cnt: Integer;
148   prgFrm: TFileProg;
149   totCnt,
150     X,
151     strmSize: LongInt;
152 begin
153   totCnt := 0;
154   {Open up the Source File to read it}
155   sStream := TFileStream.Create(SrcFile, fmOpenRead or fmShareDenyWrite);
156 
157   {Create the copying progress form and set property values}
158   prgFrm := TFileProg.Create(Application);
159   with prgFrm.Gauge1 do
160   begin
161     MinValue := 0;
162     MaxValue := 100;
163     Progress := 0;
164   end;
165   prgFrm.Show;
166 
167   {Get the size of the entire stream to use for the progress gauge. Note
168    we have to call FileSeek first because it will place the pointer
169    at the end of the file when we get the file first return value.}
170   strmSize := sStream.size;
171 
172   try
173     { Create the destination file. If it already exists,
174       overwrite it. }
175     dStream := TFileStream.Create(DstFile, fmCreate or fmShareExclusive);
176     try
177       GetMem(pBuf, bufSize);
178       try
179         {Read and write first bufSize bytes from source into the buffer
180          If the file size is smaller than the default buffer size, then
181          all the user will see is a quick flash of the progress form.}
182         cnt := sStream.read(pBuf^, bufSize);
183         cnt := dStream.write(pBuf^, cnt);
184 
185         totCnt := totCnt + cnt;
186         {Loop the process of reading and writing}
187         while (cnt > 0) do
188         begin
189           {Let things in the background proceed while loop is processing}
190           Application.ProcessMessages;
191 
192           {Read bufSize bytes from source into the buffer}
193           cnt := sStream.read(pBuf^, bufSize);
194 
195           {Now write those bytes into destination}
196           cnt := dStream.write(pBuf^, cnt);
197 
198           {Increment totCnt for progress and do arithmetic to update the
199            gauge}
200           totcnt := totcnt + cnt;
201           if not prgFrm.CancelIt then
202             with prgFrm.Gauge1 do
203             begin
204               Progress := Round((totCnt / strmSize) * 100);
205               Update;
206             end
207           else
208             Break; {If user presses cancel button, then break out of loop}
209           {which will make program go to finally blocks}
210         end;
211 
212       finally
213         FreeMem(pBuf, bufSize);
214       end;
215     finally
216       dStream.Free;
217       if prgFrm.CancelIt then {If copying was cancelled, delete the destination 
218 file}
219         DeleteFile(DstFile); {after stream has been freed, which will close the 
220 file.}
221     end;
222   finally
223     sStream.Free;
224     prgFrm.Close;
225   end;
226 end;
227 
228 procedure register;
229 begin
230   {You can change the palette entry to something of your choice}
231   RegisterComponents('BD', [TFileCopy]);
232 end;
233 
234 end.


Like the CopyFile routine in FMXUTILS.PAS, the concept behind copying for this 
component is the same: Grab a chunk of the source file, then dump it into the 
destination file. Repeat this process until all possible data has been copied over. 
Notice that I used a TFileStream once again. But this time, I didn't copy the 
entire file over in one fell swoop. That would've defeated the whole purpose of 
providing user status.

I've commented the code extensively, so I won't go into real detail here. I'll 
leave it up to you to study the code to learn about what's going on in it.

Notice the method declaration for CopyFile is declared as a virtual method. I've 
done this on purpose so that this class can be used a template class for 
specialized copy operations. The CopyFile method is actually rather trivial at this 
level -- all it does is call the DoCopyFile method and pass the FileSource and 
FileDestination property values.

However, it is the only public interface for actually performing the copying 
operation. This is an important point for all you component designers out there. 
Providing limited method visibility ensures that the core features of your 
components remain intact. Remember, you want other users of your component to see 
only what is absolutely necessary.

How is this useful? It allows you to have a bit of control over how the hierarchy 
develops. By hiding the basic functionality from descendant classes, you can ensure 
that the basic functionality of your class is retained throughout the inheritance 
tree. Granted, users can completely override the behavior of the CopyFile method, 
but that doesn't mean that the original capability will be lost. It will still be 
there, just not implemented.

Obviously, the meat of the work is performed by the DoCopyFile method. Study the 
code to see what happens from point to point. Note that I used a Pointer for the 
buffer. You can use just about any type as a buffer, but a pointer makes sense 
because its a simple 4-byte value. If you are copying a text file and want to treat 
the pointer like a string, you can cast it as a PChar, so long as you append a #0 
byte to the end of the buffer. Neat stuff, huh?

A Little Note About TFileStream

TFileStream is not a direct assignment of TStream. In fact, it's a descendant of 
THandleStream which, when created, fills a property value called Handle which is 
the handle to an external file. TFileStream inherits the Handle property. The 
significance of this is really neat: File operations that take a handle as input 
can be applied to a TFileStream. That has interesting implications in that you can 
do file operations on a TFileStream object before you write it to another place. 
Try experimenting with this.

Okay, we've come a long way. And no, I haven't delved into the depths of Stream classes. That's probably best left to another article or series of articles. In any case, play around with the TCopyFile class. It could prove to be a useful addition to your applications.

			
Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10

 

Advertisement
Share this page
Advertisement
Download from Google

Copyright © Mendozi Enterprises LLC