Link to home
Start Free TrialLog in
Avatar of bolaseal
bolaseal

asked on

Winsock - Downloading Binary File

Hello, does anyone know how to download a binary file in Delphi using Winsock? Thanks.
Avatar of gwalkeriq
gwalkeriq

Your question does not really make sense completely. A socket component, or the raw winsock interface cannot talk directrly to a remote filesystem, only a socket server. The remote socket server could be ftp, http (web), nfs, samba, wfs, etc. or a custom server that includes somewhere it its defintion, a file download feature.

The socket interface itself is inherently binary, but to get file semantics, you need to recreate something similar to ftp. If you are using Delphi, I would recommend you  consider a higher level component than the raw Winsock interface. The Indy components are a good choice for many programmers, primarily because they are free, include source, reasonable quality and support is available.

For example, Delphi 6 includes a sample client / server pair based on the Indy components (bundled with D6) named TCPStreamClient in Demos/Indy (assuming you've loaded the demos). You might also want to look at some of the Indy demos at http://www.atozed.com/indy/Demos/Indy10.iwp, in particular the Intro TCP Client should be quite educational.

If you are asking how you could download a file from a web server (for example), a more specific answer is possible. -- Of course, if all you want to do is download a file from a webserver (using winsock), it probably make more sense to use one of the higher level services available for http downloads such as using the Indy TIdHTTP component's get(url) or microsoft's api, etc.


Avatar of bolaseal

ASKER

This is just needed for downloading a GIF file. I'm pretty sure that this can be done by using Winsock to send a GET request, but I'm not sure how to receive back the data as binary data?
If you are download an image using an http get request, the image will come down in binary format. If you don't believe me, just try it. Personally, I suggest you use Indy TIdHTTP component, or the Window WinInet API functions, like this example: http://delphi.about.com/library/weekly/aa013001a.htm

The WinINet function reference is online at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wininet/wininet/wininet_reference.asp.

The ms WinInet interface has function for ftp also. Indy has a separate ftp component.

If you don't want to use one of these methods (for http, etc), or a similar high level approach it must be because you want to learn how to do everything. In which case, get the RFC on http and dive in.
I know that the image comes in a binary format, but in VB you can send the request, then get the content and save it like:

Open strSaveFile For Binary As #1
Put #1, , GetHTTPInfo(alldata, HTTP_Data_Data)
Close #1

or something similar, but if this is not possible in Delphi then I will use INet...
ok do this..

Stick a button on your form
under the [ win32 ] tab k...
Stick a progress bar on your form

Function DownloadBFile(P: Pointer): Longint; Stdcall;
Const
  CrLf: String = #$0d+#$0a;
 Var
  Fn: String;
  F: File;
  GetOut: Boolean;
  WSAData: TWSAData;
  hp: phostent;
  a: tsockaddr;
  IpAddress,Buffer: String;
  Ts,Bs,Br,ContentLength,I: Integer;
  MySock: TSocket;
Begin
  Fn:=String(P^);
//  ShowMessage('Attempting To Download File '+Fn);
  WSAStartup($0101, WSAData);
  mysock := socket(AF_INET, SOCK_stream, ipproto_tcp);
  hp := gethostbyname('www.homepages.ihug.com.au');
  Sleep(40);
    if hp = nil then
      begin
//        ShowMessage('Could Not Resolve Name...');
        exit;
      end
    else
      begin
        for i := 0 to hp^.h_length - 1 do
          IPAddress:=IpAddress+IntToStr(Ord(Hp.h_addr_list^[i]))+'.';
          SetLength(IPAddress,Length(IPaddress)-1);
        end;
  a.sin_family := AF_INET;
  a.sin_port := htons(80);
  A.sin_addr.S_addr:=inet_addr(Pchar(IpAddress));
  I:=connect(mysock, a, sizeof(a));
  buffer:='GET /~cjdelphi/'+fn+' HTTP/1.0'+CrLF+'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*'+CrLF+'Accept-Language: en-au'+CrLF+'Accept-Encoding: gzip, deflate'+CrLF+'User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'+CrLF+'Host: '+IpAddress+CrLF+'Cache-Control: no-cache'+CrLf+'Connection: Close'+CrLF+Crlf;
  I:=send(mysock, Buffer[1], Length(buffer), 0);
  Sleep(30);
  System.Assign(F,'c:\ymliteaplha.gif'); // your image to view
  System.Rewrite(F,1);
 ContentLength:=0;
 SetLength(Buffer,5048);
 FillChar(Buffer[1],Sizeof(Buffer),#0);
 BS:=recv(mysock, buffer[1], 5048,0);
 If (BS=Socket_Error) Or (BS=0) Then
   Exit;
 TS:=0;
 BS:=0;
 If (Copy(Buffer,1,15)='HTTP/1.1 200 OK') or (Copy(Buffer,1,15)='HTTP/1.0 200 OK') Then
  Begin
   Delete(Buffer,1,Pos('Content-Length:',Buffer)+15);
   ContentLength:=StrToInt(Copy(Buffer,1,Pos(#$0d+#$0a,Buffer)-1));
   Delete(Buffer,1,pos(#$0d+#$0a+#$0d+#$0a,Buffer)+3);
   Form1.ProgressBar1.Max:=ContentLength div 1024;
   If Trim(Buffer)<>'' Then
    Begin
     TS:=TS+Length(Buffer);
     BlockWrite(F,Buffer[1],Length(Buffer));
     SetLength(Buffer,5048);
    End;
  End
    else
   Exit;
 Repeat
   SetLength(Buffer,5049);
   BS:=recv(mysock, buffer[1], 5048,0);
   TS:=TS+Bs;
   Form1.ProgressBar1.Position:=TS div 1024;
   BlockWrite(F,Buffer[1],BS);
 Until (BS=0) OR (Socket_Error=BS) Or (GetOut=True);
 System.Close(F);
// 'Finished Downloading File!';
End;


Add that as a normal function

//some button click

Var
 Th: Thandle;
begin
 F:='ymlitealpha.gif'; //test image i uploaded
 CreateThread(Nil,0,@DownloadBFile,@F,0,Th);
End;


Whent the bar gets to the other end, look for c:\ymliteaplha.gif and open it, it should show a picture of my yahoo chat client i am developing...
before someone comments that  CloseSocket(MySock); is not in there, feel free to add it at the very end BUT the reason why i did not was simple, i told the webserver to close the connection after it finishes sending the file, but sure put it in after it closes of the file.
Avatar of Mohammed Nasman
Hello

  Try this function

uses URLMon;

function DownloadFile(SourceFile, DestFile: string): Boolean;
begin
  try
    Result :=  UrlDownloadToFile(nil, PChar(SourceFile), PChar(DestFile), 0,nil) = 0;
  except
    Result := False;
  end;
end;


procedure TForm1.Button2Click(Sender: TObject);
begin
  if DownloadFile('http://www.google.com/images/logo.gif'
                  ,'c:\pic.gif') then
    ShowMessage('Done')
  else
    ShowMessage('Errod downloading the file');
end;

Regards,
Mohammed
Oh, I forgot to mention...downloading from the cache is unacceptable.
look at the code i posted :|

buffer:='GET /~cjdelphi/'+fn+' HTTP/1.0'+CrLF+'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*'+CrLF+'Accept-Language: en-au'+CrLF+'Accept-Encoding: gzip, deflate'+CrLF+'User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'+CrLF+'Host: '+IpAddress+CrLF+'Cache-Control: no-cache'+CrLf+'Connection: Close'+CrLF+Crlf

'Cache-Control: no-cache'+CrLf+

THAT EXPLICITLY SAYS NO CACHE that's how the webbrowsers do it.

oh well i guess you not actually tested the code then.
I tried it, but it came up with several errors about:

TWSAData
phostent

and a few others.
then at the top of the screen add uses Winsock;

jeez, if i spend all that time writing out code for it just to be ignored like that, why do i bother....

just added Winsock to the uses clause.
function GetPage(URL: string): string;
var
       NetHandle: HINTERNET;
       UrlHandle: HINTERNET;
       Buffer: array[0..1024] of Char;
       BytesRead: DWORD;
begin
       Result := '';
       NetHandle := InternetOpen('MyWebbrowserName', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
       if Assigned(NetHandle) then
       begin
               UrlHandle := InternetOpenUrl(NetHandle, PChar(Url),
                       nil, 0, INTERNET_FLAG_RELOAD, 0);
               if Assigned(UrlHandle) then
               begin
                       FillChar(Buffer, SizeOf(Buffer), 0);
                       repeat
                               Result := Result + Buffer;
                               FillChar(Buffer, SizeOf(Buffer), 0);
                               InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer),
                                       BytesRead);
                       until BytesRead = 0;
                       InternetCloseHandle(UrlHandle);
               end
               else
               begin
                       raise Exception.CreateFmt('Error while connecting with %s', [Url]);
               end;
               InternetCloseHandle(NetHandle);
       end
       else raise Exception.Create('WinINET init error');
end;

...

Data := GetURL('www.blah.com/image.gif');
Ok, I tried your code again tobjectpascal and it gives me this error:

I:=connect(mysock, a, sizeof(a));

Incompatible types: 'TSockAddrIn' and 'PSockAddr'
What version of Delphi you using?
 

I just compiled it succesfully under Delphi 2,3 and Delphi 5..



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,Winsock,
  ComCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ProgressBar1: TProgressBar;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  F: String;


implementation

{$R *.DFM}


Function DownloadBFile(P: Pointer): Longint; Stdcall;
Const
  CrLf: String = #$0d+#$0a;
 Var
  Fn: String;
  F: File;
  GetOut: Boolean;
  WSAData: TWSAData;
  hp: phostent;
  a: tsockaddr;
  IpAddress,Buffer: String;
  Ts,Bs,Br,ContentLength,I: Integer;
  MySock: TSocket;
Begin
  Fn:=String(P^);
//  ShowMessage('Attempting To Download File '+Fn);
  WSAStartup($0101, WSAData);
  mysock := socket(AF_INET, SOCK_stream, ipproto_tcp);
  hp := gethostbyname('www.homepages.ihug.com.au');
  Sleep(40);
    if hp = nil then
      begin
//        ShowMessage('Could Not Resolve Name...');
        exit;
      end
    else
      begin
        for i := 0 to hp^.h_length - 1 do
          IPAddress:=IpAddress+IntToStr(Ord(Hp.h_addr_list^[i]))+'.';
          SetLength(IPAddress,Length(IPaddress)-1);
        end;
  a.sin_family := AF_INET;
  a.sin_port := htons(80);
  A.sin_addr.S_addr:=inet_addr(Pchar(IpAddress));
  I:=connect(mysock, a, sizeof(a));
  buffer:='GET /~cjdelphi/'+fn+' HTTP/1.0'+CrLF+'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*'+CrLF+'Accept-Language: en-au'+CrLF+'Accept-Encoding: gzip, deflate'+CrLF+'User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'+CrLF+'Host: '+IpAddress+CrLF+'Cache-Control: no-cache'+CrLf+'Connection: Close'+CrLF+Crlf;
  I:=send(mysock, Buffer[1], Length(buffer), 0);
  Sleep(30);
  System.Assign(F,'c:\ymliteaplha.gif'); // your image to view
  System.Rewrite(F,1);
 ContentLength:=0;
 SetLength(Buffer,5048);
 FillChar(Buffer[1],Sizeof(Buffer),#0);
 BS:=recv(mysock, buffer[1], 5048,0);
 If (BS=Socket_Error) Or (BS=0) Then
   Exit;
 TS:=0;
 BS:=0;
 If (Copy(Buffer,1,15)='HTTP/1.1 200 OK') or (Copy(Buffer,1,15)='HTTP/1.0 200 OK') Then
  Begin
   Delete(Buffer,1,Pos('Content-Length:',Buffer)+15);
   ContentLength:=StrToInt(Copy(Buffer,1,Pos(#$0d+#$0a,Buffer)-1));
   Delete(Buffer,1,pos(#$0d+#$0a+#$0d+#$0a,Buffer)+3);
   Form1.ProgressBar1.Max:=ContentLength div 1024;
   If Trim(Buffer)<>'' Then
    Begin
     TS:=TS+Length(Buffer);
     BlockWrite(F,Buffer[1],Length(Buffer));
     SetLength(Buffer,5048);
    End;
  End
    else
   Exit;
 Repeat
   SetLength(Buffer,5049);
   BS:=recv(mysock, buffer[1], 5048,0);
   TS:=TS+Bs;
   Form1.ProgressBar1.Position:=TS div 1024;
   BlockWrite(F,Buffer[1],BS);
 Until (BS=0) OR (Socket_Error=BS) Or (GetOut=True);
 System.Close(F);
// 'Finished Downloading File!';
End;

procedure TForm1.Button1Click(Sender: TObject);
Var
 Th: Thandle;
begin
 F:='ymlitealpha.gif'; //test image i uploaded
 CreateThread(Nil,0,@DownloadBFile,@F,0,Th);
end;

end.
7
Maybe it's a different version of the Winsock.pas file?
ASKER CERTIFIED SOLUTION
Avatar of tuqui
tuqui
Flag of Japan image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks tuqui, I tried that and it works well.

Also thanks tobjectpascal, but that code still didn't work... I give you points anyway for helping :)
after changing that line what was the error message? look there's only 1 error IT CAN BE FIXED i just need to know which Version of Delphi you're using.
It's Delphi 7, and after changing that line it was the same error as before:

Incompatible types: 'TSockAddrIn' and 'PSockAddr'
i have Delphi 7 trial, i will install it in a bit, i can't see how it's a problem.

but with D7, you also have TClientSocket/TcpSocket which will do the job fine.