Mason Wheeler
Mason Wheeler

Reputation: 84550

How do I make a page that creates a file download work in Indy?

I've got an Indy server, and having it send a file when the URL requests a file is trivial and always seems to work right.

But I'm also trying to set up a second case that looks like this:

http://example.com/getFile.html?filename=myImage.png

(This works with dynamically generated content, so asking for a static file wouldn't work.) I'm using a custom Indy client and calling TIdHTTP.Get to retrieve all these files, and in the second use case, it seems to be following this algorithm:

Roll 1D4.  If you rolled 2 or higher, download correctly.
Otherwise, roll 1D6 against this table of Indy exceptions and
raise one of them.
If a random exception is raised, it MUST NOT be caught inside
of Indy code.

So far the common ones seem to be EIdHTTPProtocolException, EIdClosedSocket, and EIdNotConnected, but I've seen a few others as well. Setting up an exception handler to catch these and retry the download works sometimes, and other times it just raises more exceptions, again seemingly at random with no deterministic principle behind it.

The fact that this never happens using the static download use case tells me that there's probably something strange going on under the hood in the second use case that my code isn't accounting for properly.

The code is really quite simple. On the client side:

procedure TdmConnection.GetFile(const URL: string; stream: TStream);

  procedure Retry;
  begin
     sleep(100);
     stream.Size := 0; //clear any aborted partial download
     http.Get(URL, stream);
  end;

begin
   try
      http.Get(URL, stream);
   except
      on EIdClosedSocket do
        Retry;
      on EIdNotConnected do
        Retry;
      on EIdHTTPProtocolException do
        Retry;
   end;
end;

And on the server side, the request runs a script that produces the file (correctly; I've verified that part) and tells the Indy server to return it as the HTTP response. I've tried simply opening a TFileStream and assigning it to Response.ContentStream, and I've tried calling the ServeFile method on the Response object, and I still end up getting the same errors on the client side. The relevant part of the server code looks like this:

  if lServeFile <> '' then begin //variable set by the script
     if lServeFile = '***' then
        response.ResponseNo := 404
     else begin
       response.ResponseNo := 200;
       response.ServeFile(AContext, lServeFile);
     end;
  end;

Any experienced Indy users out there know what I'm doing wrong? Using stock Indy 10 that came with Delphi XE.

Upvotes: 2

Views: 1054

Answers (1)

Mason Wheeler
Mason Wheeler

Reputation: 84550

Nevermind, the problem wasn't in Indy. Upon further debugging I eventually figured out that there were two threads both using the IdHTTP component, and sometimes they were using it at the same time. Guaranteeing that each thread would have its own IdHTTP fixed the problem.

Upvotes: 1

Related Questions