Delphi - MemoryStream或FileStream

7
我正在使用Indy(idHTTP)从互联网下载一个EXE文件,我可以使用内存流或文件流将其保存到磁盘,但我真的不知道它们之间是否有任何区别(也许在文件的结果结构中?)。我还没有找到答案。
这里有两个简单的函数来模拟我正在做的事情:
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;

你们专家认为使用内存流(memorystream)还是文件流(filestream)有什么区别?在使用一种或另一种类型时,EXE文件的结构是否有任何差异?哪种类型被推荐使用?

谢谢!祝你周末愉快!


4
TMemoryStream 内部使用 TFileStream 将数据保存到文件(用于 SaveToFile 方法),因此答案很简单 - 使用 TFileStream - TLama
1
使用TMemoryStream而不是TFileStream的原因可能有两个:一是避免在发生异常时清理文件系统,二是需要在保存文件之前进行一些操作。 - bummi
8
你肆意使用 with 的做法让我感到害怕。我建议你停止这样做。 - David Heffernan
@bummi,我正在创建一个安装程序,从我的网站下载应用程序文件,我认为你说的使用MemoryStream是正确的,因为连接可能会失败并生成异常。 - Guybrush
@DavidHeffernan,谢谢您的建议。下次我会注意的。 - Guybrush
1
@bummi,我会说这些方法都有一些缺点。一方面会导致临时文件混乱,另一方面则是占用大量内存。 - OnTheFly
2个回答

8

TMemoryStreamTFileStream在流的角度上没有区别。它们都是流并保存一串字节,都是从TStream派生的。

你可以像这样实现通用函数:

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;

使用TFileStream调用它。

var
  LStream : TStream;

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

或者使用 TMemoryStream 或其他您喜欢的流实例。

1
这确实是代码应该被重构的方式。但我认为问题不在于代码,而在于设计。似乎问题是应该使用内存流还是文件流来实现这个目的。 - David Heffernan
1
@DavidHeffernan:“我正在从互联网下载一个EXE文件...使用一种或另一种类型时,EXE文件的结构是否有任何区别?” OP想知道TMemoryStream是否会以不同于TFileStream的方式存储数据 :o) - Sir Rufo
我没有那样阅读这个问题,我想是因为任何人都认为将流存储到磁盘而不是内存中时,流的内容会发生变化,这似乎很奇怪。这太奇怪了! - David Heffernan

2
在很多情况下,在下载和文件之间放置中间内存流是没有意义的。这样做只会消耗内存,因为在将文件写入磁盘之前,您必须将整个文件放入内存中。直接使用文件流可以避免这个问题。
文件流选项的主要问题出现在您想要确保在保存到磁盘之前已经成功下载了整个文件的情况下。例如,如果您要覆盖先前版本的文件,则可能需要下载它、检查哈希签名,然后再覆盖原始文件。在这种情况下,您需要将文件放置到某个临时位置以便覆盖。您可以使用一个内存流,或者使用一个带有临时文件名的文件流。

是的,我正在创建一个安装程序,也许用户会更新文件。所以我想我会使用MemoryStream,因为文件不是很大(2或3 MB)。谢谢! - Guybrush
@Paruba 如果你愿意,仍然可以使用文件流。只需使用临时文件名即可。当你确认文件已下载完成后,删除旧文件并将临时文件重命名即可。 - David Heffernan
9
使用TFileStream有另一个好处-能够在不从头开始的情况下恢复中断的下载。如果HTTP服务器支持字节范围,您可以配置TIdHTTP告诉服务器发送远程文件的哪个部分,而不是整个文件。对于小文件,这并不重要,但对于大文件而言,甚至可以使用多个线程同时下载同一文件的不同部分。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接