使用TIdHttp逐步下载文件

时间:2012-11-30 07:53:12

标签: delphi http download indy idhttp

我想使用TIdHttp(Indy10)实现一个简单的http下载器。我从互联网上找到了两种代码示例。不幸的是,他们都没有100%满足我。这是代码,我想要一些建议。


变体1

var
  Buffer: TFileStream;
  HttpClient: TIdHttp;
begin
  Buffer := TFileStream.Create('somefile.exe', fmCreate or fmShareDenyWrite);
  try
    HttpClient := TIdHttp.Create(nil);
    try
      HttpClient.Get('http://somewhere.com/somefile.exe', Buffer); // wait until it is done
    finally
      HttpClient.Free;
    end;
  finally
    Buffer.Free;
  end;
end;

代码紧凑且易于理解。问题是它在下载开始时分配磁盘空间。另一个问题是我们无法直接在GUI中显示下载进度,除非代码在后台线程中执行(或者我们可以绑定HttpClient.OnWork事件)。


变体2:

const
  RECV_BUFFER_SIZE = 32768;
var
  HttpClient: TIdHttp;
  FileSize: Int64;
  Buffer: TMemoryStream;
begin
  HttpClient := TIdHttp.Create(nil);
  try
    HttpClient.Head('http://somewhere.com/somefile.exe');
    FileSize := HttpClient.Response.ContentLength;

    Buffer := TMemoryStream.Create;
    try
      while Buffer.Size < FileSize do
      begin
        HttpClient.Request.ContentRangeStart := Buffer.Size;
        if Buffer.Size + RECV_BUFFER_SIZE < FileSize then
          HttpClient.Request.ContentRangeEnd := Buffer.Size + RECV_BUFFER_SIZE - 1
        else
          HttpClient.Request.ContentRangeEnd := FileSize;

        HttpClient.Get(HttpClient.URL.URI, Buffer); // wait until it is done
        Buffer.SaveToFile('somefile.exe');
      end;
    finally
      Buffer.Free;
    end;
  finally
    HttpClient.Free;
  end;
end;

首先我们从服务器查询文件大小,然后我们下载文件内容。检索到的文件内容将在完全收到后保存到磁盘。潜在的问题是我们必须向服务器发送多个GET请求。我不确定某些服务器(例如megaupload)是否可能限制特定时间段内的请求数量。


我的期望

  1. 下载程序只应向服务器发送一个GET请求。
  2. 下载开始时不得分配磁盘空间。
  3. 感谢任何提示。

3 个答案:

答案 0 :(得分:26)

变体#1是最简单的,也就是Indy的使用方式。

关于磁盘分配问题,您可以从TFileStream派生一个新类,并覆盖其SetSize()方法,不执行任何操作。 TIdHTTP仍将在适当时尝试预分配文件,但实际上不会分配任何磁盘空间。写入TFileStream将根据需要增加文件。

关于状态报告,TIdHTTP为此目的有OnWork...个事件。 AWorkCountMax的{​​{1}}参数将是已知的实际文件大小(响应未分块),如果未知,则为0。 OnWorkBegin事件的AWorkCount参数将是到目前为止已传输的累计字节数。如果文件大小已知,则只需将OnWork除以AWorkCount并乘以100即可显示总百分比,否则只需显示AWorkCountMax值。如果您想显示转移的速度,可以根据AWorkCount值与多个AWorkCount事件之间的时间间隔的差异来计算。

试试这个:

OnWork

type
  TNoPresizeFileStream = class(TFileStream)
  procedure
    procedure SetSize(const NewSize: Int64); override;
  end;

procedure TNoPresizeFileStream.SetSize(const NewSize: Int64);
begin
end;

答案 1 :(得分:4)

以下是一个示例,说明如何使用OnWork组件显示进度条:

Download a File from internet programatically with an Progress event using Delphi and Indy

您不必担心磁盘分配。分配的磁盘空间实际上并未写入,因此不会损坏磁盘。很高兴它被分配,以便其他进程无法声明磁盘空间并让你的空间不足!

答案 2 :(得分:2)

不要忘记为Variant 2添加此内容

 : Else HttpClient.Request.ContentRangeEnd := FileSize;

替换

   if Buffer.Size + RECV_BUFFER_SIZE < FileSize then
  HttpClient.Request.ContentRangeEnd := Buffer.Size + RECV_BUFFER_SIZE - 1;

通过

   if Buffer.Size + RECV_BUFFER_SIZE < FileSize then
  HttpClient.Request.ContentRangeEnd := Buffer.Size + RECV_BUFFER_SIZE - 1;
   Else HttpClient.Request.ContentRangeEnd := FileSize;