Delphi Clinic | C++Builder Gate | Training & Consultancy | Delphi Notes Weblog | Dr.Bob's Webshop |
|
As most of you will know by now, we can write all kinds of wondrous internet applications with Delphi. Especially with the new Delphi 3 features such as ActiveForms and Web Modules. But sometimes, we want to do something that shouldn't be fancy or hard, but just quick and easy. Sometimes we just want to download a file from the internet. And in internet terms, this means we'd want to use an FTP (file transfer protocol) client. And if you're like me, you don't just use (or trust) any FTP client, no, if you're like me, you want to write your own...
FTP
As I said in the introduction, FTP stands for File Transfer Protocol, which is formally described in RFC 959.
The FTP communication model can be implemented using sockets, although this is a rather low-level approach (and if you read the specs, you'll soon realise that writing your FTP client program from scratch based on sockets is not an easy task).
On the other hand, we'll have the NetManage TFTP component in Delphi 2.01 (and up) and C++Builder.
However, this is where the trust comes in.
I've tried to use this component several times, and it's just plain buggy (it chops off files bigger than 10 Kb for example).
I can understand why Microsoft (who initially developed the Internet Solutions Pack) didn't want them and sold them to NetManage, who wouldn't manage them and now sold them to NetMasters.
The trouble is that the Internet Solutions Pack - while free - consists of a set of rather limited internet development ActiveX controls, and so far each company that offers them also has a better solution (usually not free).
So support and documentation has always been lacking...
So, back to the low-level socket approach? Not quite.
This time it's Microsoft who's coming to our rescue (that must be a rather unique experience for some of us, eh?)
WININET
Some time ago, Microsoft released WININET, which is nothing more than a layer on top of low-level Internet APIs especially for Win32 programmers.
WININET offers a higher level interface to otherwise low-level protocols such as HTTP and FTP.
The use is really easy, and the best thing is: the WININET.PAS unit with the API definitions in ObjectPascal is already included with your copy of Delphi 2.x or higher!
There's also a big document describing all WININET APIs in detail, which can be found on Microsoft's website (the location has changed from time to time, so it's best to use their search engine to locate it if you want to download the latest copy).
DrBobFTP
WININET uses something which they call an "internet handle" (much like Windows uses Windows handles), and all APIs either need or return such a handle.
For example, to open a new WININET session, we need to call InternetOpen, which returns a handle which we must use (and pass to other APIs) until the end of our session.
To close any internet handle, we must always call InternetCloseHandle (so after we've received a handle we can use, we should immediate write a try-finally block, where we close the handle in the finally part again).
To open a remote file (or URL) on the internet, we must call InternetOpenURL, which again returns an internet handle.
Now, to download that remote file (URL) to our local machine, we only have to make a number of calls to InternetReadFile, which works a bit like BlockRead, in that it copies data from the remote file to a data buffer.
We can use BlockWrite to write the data from the buffer to a local file, and hence with only three WININET functions (four if you count the closing InternetCloseHandle function), we can write a basic but fast FTP client program as follows:
program DrBobFTP; {$APPTYPE CONSOLE} {$I+} uses Windows, WinINet; procedure CopyURL(const URL, OutputFile: String); const BufferSize = 1024; var hSession, hURL: HInternet; Buffer: Array[0..Pred(BufferSize)] of Byte; BufferLength: DWORD; f: File; begin hSession := InternetOpen('DrBob',INTERNET_OPEN_TYPE_PRECONFIG,nil,nil,0); try hURL := InternetOpenURL(hSession, PChar(URL), nil,0,0,0); try Assign(f, OutputFile); Rewrite(f,1); repeat InternetReadFile(hURL, @Buffer, BufferSize, BufferLength); write('.'); BlockWrite(f, Buffer, BufferLength) until BufferLength = 0; Close(f); writeln('OK') { if we get here, we succeeded } finally InternetCloseHandle(hURL) end finally InternetCloseHandle(hSession) end end; begin if ParamCount < 2 then begin writeln('Usage: DrBobFTP URL Filename'); writeln('Example: DrBobFTP http://www.drbob42.com/ftp/headconv.zip hc.zip') end else CopyURL(ParamStr(1), ParamStr(2)) end.Of course, in order to execute this little program you also need the WININET.DLL, which can be found on the Microsoft website.
Enhancements?
If you read the WININET documentation, you'll see that there are also APIs for FindFile, so you can actually create an overview of the available remote files.
And based on that information, you can start to write your own web robot that can download part of a website (for example those files that are new of have changed since your last visit).
All automatically, and without GUI (but fast).