[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 349
  • Last Modified:

Downloading file from the net...

Hi. I'm trying the example bellow which works fine only with the HTTP address and " LocalFileName" specified by default; i would like to get files from everywhere and get their own names(followed by their  proper extension of course). in another words... save the files with their real names. Can someone give me a help ? Thanks !!

http://delphi.about.com/od/internetintranet/l/aa013001a.htm
0
zafira12
Asked:
zafira12
  • 3
  • 2
1 Solution
 
Russell LibbySoftware Engineer, Advisory Commented:
You should be aware that for some url's, you will not be able to get the actual file name as it stored on the server. Take for example:

http://www.google.com

The result is an html page (.htm), but as to the actual file name? That becomes your choice as to what to save it as. For those urls that do specify a filename (url), then the following code will get the filename and use that when storing local. I also set the code so that if the filename is empty, it will default to "default.htm", as in the above google example:

function ExtractURLFileName(const FileName: string): string;
var  lpComponents:  TURLComponents;
     lpBuffers:     Array [0..5, 0..1024] of Char;
begin

  // Crack the url into components
  lpComponents.dwStructSize:=SizeOf(TURLComponents);
  lpComponents.lpszScheme:=@lpBuffers[0, 0];
  lpComponents.dwSchemeLength:=1024;
  lpComponents.lpszHostName:=@lpBuffers[1, 0];
  lpComponents.dwHostNameLength:=1024;
  lpComponents.nPort:=0;
  lpComponents.lpszUserName:=@lpBuffers[2, 0];
  lpComponents.dwUserNameLength:=1024;
  lpComponents.lpszPassword:=@lpBuffers[3, 0];
  lpComponents.dwPasswordLength:=1024;
  lpComponents.lpszUrlPath:=@lpBuffers[4, 0];
  lpComponents.dwUrlPathLength:=1024;
  lpComponents.lpszExtraInfo:=@lpBuffers[5, 0];
  lpComponents.dwExtraInfoLength:=1024;

  if InternetCrackUrl(PChar(FileName), Length(FileName), ICU_DECODE, lpComponents) then
  begin
     result:=lpBuffers[4];
     if (Length(result) = 0) then
        result:='default.htm'
     else if (ExtractFileExt(result) = EmptyStr) then
        result:=result+'.htm';
  end
  else
     result:='default.htm';

end;

function GetInetFile(URL, LocalPath: String): Boolean;
const BufferSize = 1024;
var
  hSession, hURL:   HInternet;
  Buffer:           Array [0..Pred(BufferSize)] of Byte;
  BufferLen:        DWORD;
  FileName:         String;
  f:                File;
begin

  hSession:=InternetOpen(PChar(ExtractFileName(Application.ExeName)), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
     hURL:=InternetOpenURL(hSession, PChar(URL), nil, 0, 0, 0);
     try
        FileName:=ExcludeTrailingBackslash(LocalPath)+'\'+ExtractURLFileName(URL);
        AssignFile(f, FileName);
        Rewrite(f, 1);
        repeat
           if InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen) then
              BlockWrite(f, Buffer, BufferLen)
           else
              break;
        until (BufferLen = 0);
        CloseFile(f);
        result:=True;
     finally
        InternetCloseHandle(hURL);
     end
  finally
     InternetCloseHandle(hSession);
  end;

end;

// All that needs to be passed is the URL address and the local  path to save to

GetInetFile('http://www.google.com', 'c:\');

// this will save the file to
// c:\default.htm

Regards,
Russell


0
 
kyrleanCommented:
Your code works great Russel. Thank you very much.Let me just make things clear in my head. All the addresses i tryed were saved as .htm .
There's no way i can provide the app an address and get the file right ??? (the original method presented by me above is excluded of course.....)
Another one... can you add some comments through the codes in order to better understand what has been done ??
Thanks again, and i apologize for asking too much !
0
 
Russell LibbySoftware Engineer, Advisory Commented:

1.) Correct. On some addresses, there will be NO way to know the actual filename. In some cases, the server will generate the page through code alone (no physical file). For example, any default site address:

eg:
http://www.experts-exchange.com
http://www.google.com
http://www.microsoft.com

Will all return an html page, but as to the file name on the server? One can really only guess. And then there are asp pages, pages generated from isapi modules (dll?query=etc), cgi programs, etc. For those, you may get a filename, but it is the filename of the program or library that generated the page. For some, the page is dynamic and does not even exist as a filename; its generated through code alone.

2.) I am including the fully commented code, as well as slight fix for ExtractURLFileName. To test it (in this case, it will get the correct file name because its in the url)

  GetInetFile('http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_21402136.html#13864176', 'c:\');


3.) Don't apologize. Its good that you have the desire to understand HOW it works.


Regards,
Russell

--------------


function ExtractURLFileName(const FileName: string): string;
var  lpComponents:  TURLComponents;
     lpBuffers:     Array [0..5, 0..1024] of Char;
begin

  // The InternetCrackUrl API will handle the splitting of the URL address
  // into its respective components. The PChar settings all require a buffer
  // address, so we pass it a 2 dimensional array. The first dim is for the buffer,
  // the second is the buffer size (6 buffers of 1KB each).

  // Set structure size
  lpComponents.dwStructSize:=SizeOf(TURLComponents);

  // Set buffer and size for scheme (eg http)
  lpComponents.lpszScheme:=@lpBuffers[0, 0];
  lpComponents.dwSchemeLength:=1024;

  // Set buffer and size for host name
  lpComponents.lpszHostName:=@lpBuffers[1, 0];
  lpComponents.dwHostNameLength:=1024;

  // Set default port (this will be filled in by result)
  lpComponents.nPort:=0;

  // Set buffer and size for user name
  lpComponents.lpszUserName:=@lpBuffers[2, 0];
  lpComponents.dwUserNameLength:=1024;

  // Set buffer and size for password
  lpComponents.lpszPassword:=@lpBuffers[3, 0];
  lpComponents.dwPasswordLength:=1024;

  // Set buffer and size for url path. THIS is the component that we are really after
  lpComponents.lpszUrlPath:=@lpBuffers[4, 0];
  lpComponents.dwUrlPathLength:=1024;

  // Set buffer and size for extra info (eg ?apples=test)
  lpComponents.lpszExtraInfo:=@lpBuffers[5, 0];
  lpComponents.dwExtraInfoLength:=1024;

  // Split the url into its components using the DECODE flag
  if InternetCrackUrl(PChar(FileName), Length(FileName), ICU_DECODE, lpComponents) then
  begin
     // Get the url path
     result:=lpBuffers[4];
     // If blank (for example, the address was http://www.google.com)
     if (Length(result) = 0) then
        // Set default.htm
        result:='default.htm'
     // Else (eg http://www.experts-exchange.com/Programming/Programming_Languages/Delphi)
     else
     begin
        // Copy just the last path (eg http://www.experts-exchange.com/Programming/Programming_Languages/Delphi -> Delphi)
        result:=Copy(result, Succ(LastDelimiter('/', result)), MaxInt);
        // Check for extension; if no extension then append a .htm to the file name
        if (ExtractFileExt(result) = EmptyStr) then result:=result+'.htm';
     end;
  end
  else
     // Failed to split the url (the url is most likely invalid to start with)
     result:='default.htm';

end;

function GetInetFile(URL, LocalPath: String): Boolean;
const BufferSize = 1024;
var
  hSession, hURL:   HInternet;
  Buffer:           Array [0..Pred(BufferSize)] of Byte;
  BufferLen:        DWORD;
  FileName:         String;
  f:                File;
begin

  // Open am internet session using the default IE configurations
  hSession:=InternetOpen(PChar(ExtractFileName(Application.ExeName)), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
     // Open a synchronous connection to the url using the session handle from above
     hURL:=InternetOpenURL(hSession, PChar(URL), nil, 0, 0, 0);
     try
        // Create the local filename by using the specified local path PLUS the extracted URL file name
        FileName:=ExcludeTrailingBackslash(LocalPath)+'\'+ExtractURLFileName(URL);
        // Assign file name to file
        AssignFile(f, FileName);
        // Rewrite the file (specifiy rec size as one)
        Rewrite(f, 1);
        // Loop until a.) InternetReadFile fails or b.) InternetReadFile returns 0 indicating no more data
        repeat
           // Read from the connection
           if InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen) then
              // Write the data to the local file
              BlockWrite(f, Buffer, BufferLen)
           else
              // Failed to read, break the loop
              break;
        until (BufferLen = 0);
        // Close the local file
        CloseFile(f);
        // Success
        result:=True;
     finally
        // Close the connection handle
        InternetCloseHandle(hURL);
     end
  finally
     // Close the session handle
     InternetCloseHandle(hSession);
  end;

end;
0
 
kyrleanCommented:
Great man. I tested it. This link for exemple got the file from ' http://delphi.about.com/library/forminbpl.zip"  as forminbpl.zip . Without any specification like => LocalFileName:='File Downloaded From the Net.zip' which is included on the example project at the web page  mentioned above.
Thank you very much !!!!!!!
I'll keep making efforts to become a great coder like you :-)   ! ! !
0
 
Russell LibbySoftware Engineer, Advisory Commented:

Thanks, and glad it does what you are after. ;-)

Russell
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now