7

I am downloading an EXE file from internet using Indy (idHTTP), and I can use memorystream or filestream to save it to disk, but I really do not know if there is any difference between them (maybe in the result structure of the file?). I could't find yet an answer for this.

Where, here are 2 simple functions to simulate what I am doing:

Function DownloadMS(FUrl, Dest: String): Boolean;
var
  Http: TIdHTTP;
  Strm: TMemoryStream;
Begin
  Result := False;
  Http := TIdHTTP.Create;
  Strm := TMemoryStream.Create;
  With Http, Strm Do
  Try
    Try
      Get(FUrl, Strm);
      If (Size > 0) Then
      Begin
        Position := 0;
        SaveToFile(Dest);
        Result := True;
      end;
    Except
    end;
  Finally
    Strm.Free;
    Http.Free;
  end;
end;

Function DownloadFS(FUrl, Dest: String): Boolean;
var
  Http: TIdHTTP;
  Strm: TFileStream;
Begin
  Result := False;
  Http := TIdHTTP.Create;
  Strm := TFileStream.Create(Dest, fmCreate);
  With Http, Strm Do
  Try
    Try
      Get(FUrl, Strm);
      Result := (Size > 0);
    Except
    end;
  Finally
    Strm.Free;
    Http.Free;
  end;
end;

What you experts think about using one or other type (memorystream or filestream)? Is there any difference in the structure of the EXE file when using one or other type? What type is recommended?

Thank you! Have a nice weekend!

Guybrush
  • 1,575
  • 2
  • 27
  • 51
  • 4
    The `TMemoryStream` uses internally `TFileStream` for saving to file (for the `SaveToFile` method), so the answer is pretty simple - use `TFileStream`. – TLama May 04 '13 at 14:08
  • 1
    I could think about 2 reasons for using TMemoryStream instead of TFileStream. Avoiding a cleanup in the filesystem in case of an exception and the need of some manipulations before saving it to a file. – bummi May 04 '13 at 14:14
  • 8
    Your wanton use of `with` makes me scared. I'd recommend that you stop doing that. – David Heffernan May 04 '13 at 14:20
  • @bummi, I am creating an installer that download application files from my site, and I think you are right about using memorystream, because connection may fail and generate an exception. – Guybrush May 04 '13 at 15:02
  • @DavidHeffernan, thank you for the advise. I will take care next time. – Guybrush May 04 '13 at 15:02
  • 1
    @bummi, I'd say there are drawbacks for the approaches, temporary file mess on the one side and memory hogging on the other side. – OnTheFly May 04 '13 at 16:01

2 Answers2

8

There is no difference between TMemoryStream or TFileStream from the stream point of view.

They are both streams and hold a stream of bytes and are both derived from TStream.

You can implement your function generalized like this

function DownloadToStream( const AUrl : String; ADest : TStream ): Boolean;
var
  LHttp: TIdHTTP;
begin
  LHttp := TIdHTTP.Create;
  try
    LHttp.Get( AUrl, ADest );
    Result := ADest.Size > 0;
  finally
    LHttp.Free;
  end;
end;

and call it with a TFileStream

var
  LStream : TStream;

begin
  LStream := TFileStream.Create( 'MyFile.exe', fmCreate );
  if DownloadToStream( '', LStream ) then
    ...
end;

or TMemoryStream or whatever stream instance you like

Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • 1
    That is indeed how the code should be re-factored. But I think the question is not so much about code, but about design. Should memory stream or file stream be used for this purpose seems to be the question. – David Heffernan May 04 '13 at 14:47
  • 1
    @DavidHeffernan "I am downloading an **EXE file** from internet ... Is there any difference in the structure of the **EXE file** when using one or other type?" OP is wondering if `TMemoryStream` will store the data different than `TFileStream` would do :o) – Sir Rufo May 04 '13 at 15:12
  • I didn't read the question that way, I suppose because it seems bizarre that anyone could think that the content of a stream is changed when you store it to disk rather than memory. That's just weird! – David Heffernan May 04 '13 at 15:16
2

In many cases there will be no point in putting an intermediate memory stream in between the download and the file. All that will do is consume memory because you have to put the entire file in memory before you can put it to disk. Using a file stream directly avoids that issue.

The main situation where the file stream option has problems is if you want to be sure that you've downloaded the entire file successfully before saving to disk. For example, if you are overwriting a previous version of a file, you may want to download it, check a hash signature, and only then overwrite the original file. In that scenario you need to put the file to some temporary location before over-writing. You could use a memory stream, or you could use a file stream using a temporary file name.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Yes, I am creating an installer, and maybe user will be updating the files. So I think I will use MemoryStream, because files are not so big (2 or 3 MB). Thank you! – Guybrush May 04 '13 at 15:05
  • @Paruba You can still use a file stream if you wish. Just use a temporary file name. When you are happy the file downloaded, delete the old and rename the temp file. – David Heffernan May 04 '13 at 15:06
  • 9
    Using a `TFileStream` has another benefit - the ability to resume a broken download without starting over from scratch. If the HTTP server supports byte ranges, you can configure `TIdHTTP` to tell the server what portion of the remote file to send, instead of the entire file. For small files, this does not matter much, but for larger files it can, even the ability to download different sections of the same file using multiple threads at one time. – Remy Lebeau May 04 '13 at 17:57