Delphi 使用TIdHttp逐步下载文件

Delphi 使用TIdHttp逐步下载文件,delphi,http,download,indy,idhttp,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 := TIdHtt

我想使用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;
const
RECV_BUFFER_SIZE=32768;
变量
HttpClient:TIdHttp;
文件大小:Int64;
缓冲区:TMemoryStream;
开始
HttpClient:=TIdHttp.Create(nil);
尝试
HttpClient.Head('http://somewhere.com/somefile.exe');
FileSize:=HttpClient.Response.ContentLength;
缓冲区:=TMemoryStream.Create;
尝试
而Buffer.Size
首先,我们从服务器查询文件大小,然后将文件内容分块下载。检索到的文件内容完全接收后将保存到磁盘。潜在的问题是我们必须向服务器发送多个GET请求。我不确定某些服务器(如megaupload)是否会限制特定时间段内的请求数量


我的期望

  • 下载程序应该只向服务器发送一个GET请求
  • 下载开始时,不得分配磁盘空间

  • 如有任何提示,我们将不胜感激

    下面是一个示例,演示如何使用组件OnWork来显示进度条:

    您不应该担心磁盘分配。分配的磁盘空间实际上没有写入,因此不会损坏磁盘。很高兴它被分配了,这样就不可能有另一个进程占用磁盘空间而让您耗尽空间

    变体#1是最简单的,也是印第的用意

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

    关于状态报告,
    TIdHTTP
    OnWork…
    事件用于此目的。
    OnWorkBegin
    AWorkCountMax
    参数将是实际文件大小(如果已知)(响应未分块),如果未知,则为0。
    OnWork
    事件的
    AWorkCount
    参数将是迄今为止已传输的累积字节数。如果已知文件大小,只需将
    AWorkCount
    除以
    AWorkCountMax
    并乘以100即可显示总百分比,否则只需显示
    AWorkCount
    值本身即可。如果要显示传输速度,可以根据
    a工作计数
    值与多个
    on work
    事件之间的时间间隔之差来计算传输速度

    试试这个:

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

    类型
    TSomeClass=class(TSomething)
    ...
    TotalBytes:In64;
    LastWorkCount:Int64;
    拉斯蒂克:长单词;
    程序下载;
    过程HttpWorkBegin(ASender:ToObject;AWorkMode:TWorkMode;AWorkCountMax:Int64);
    过程HttpWork(ASender:ToObject;AWorkMode:TWorkMode;AWorkCount:Int64);
    过程HttpWorkEnd(ASender:ToObject;AWorkMode:TWorkMode);
    ...
    结束;
    程序TSomeClass.下载;
    变量
    缓冲区:TNoPresizeFileStream;
    HttpClient:TIdHttp;
    开始
    缓冲区:=TNoPresizeFileStream.Create('somefile.exe',fmCreate或fmShareDenyWrite);
    尝试
    HttpClient:=TIdHttp.Create(nil);
    尝试
    HttpClient.OnWorkBegin:=HttpWorkBegin;
    HttpClient.OnWork:=HttpWork;
    HttpClient.OnWorkEnd:=HttpWorkEnd;
    HttpClient.Get('http://somewhere.com/somefile.exe“,缓冲区);//等到它完成
    最后
    HttpClient.Free;
    结束;
    最后
    缓冲。免费;
    结束;
    结束;
    过程TSomeClass.HttpWorkBegin(ASender:TObject;AWorkMode:TWorkMode;AWorkCountMax:Int64);
    开始
    如果工作模式为wmRead,则退出;
    //根据需要初始化状态UI。。。
    //
    //如果TIdHTTP正在主线程中运行,请更新您的UI
    //组件,然后调用窗体的
    //Update()方法来执行重新绘制,或Application.ProcessMessages()
    //处理其他UI操作,如按钮按下(用于
    //例如,取消下载)。
    //
    //如果TIdHTTP正在工作线程中运行,请使用TIdNotify
    //或TIdSync类来根据需要更新UI组件,以及
    //让操作系统正常地分派重绘和其他消息。。。
    TotalBytes:=AWorkCountMax;
    LastWorkCount:=0;
    LastTicks:=滴答声;
    结束;
    过程TSomeClass.HttpWork(ASender:TObject;AWorkMode:TWorkMode;AWorkCount:Int64);
    变量
    完成百分比:整数;
    ElapsedMS:长单词;
    ByTestTransferred:Int64;
    字节数:Int64;
    开始
    如果工作模式为wmRead,则退出;
    ElapsedMS:=GetTickDiff(LastTicks,Ticks);
    如果ElapsedMS=0,则ElapsedMS:=1;//避免EDivByZero错误
    如果TotalBytes>0,则
    完成百分比:=(双倍(工作计数)/总字节)*100.0;
    其他的
    完成百分比:=0.0;
    ByTestTransferred:=AWorkCount-LastWorkCount
    
    type
      TSomeClass = class(TSomething)
      ...
        TotalBytes: In64;
        LastWorkCount: Int64;
        LastTicks: LongWord;
        procedure Download;
        procedure HttpWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
        procedure HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
        procedure HttpWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
      ...
      end;
    
    procedure TSomeClass.Download;
    var
      Buffer: TNoPresizeFileStream;
      HttpClient: TIdHttp;
    begin
      Buffer := TNoPresizeFileStream.Create('somefile.exe', fmCreate or fmShareDenyWrite);
      try
        HttpClient := TIdHttp.Create(nil);
        try
          HttpClient.OnWorkBegin := HttpWorkBegin;
          HttpClient.OnWork := HttpWork;
          HttpClient.OnWorkEnd := HttpWorkEnd;
    
          HttpClient.Get('http://somewhere.com/somefile.exe', Buffer); // wait until it is done
        finally
          HttpClient.Free;
        end;
      finally
        Buffer.Free;
      end;
    end;
    
    procedure TSomeClass.HttpWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
    begin
      if AWorkMode <> wmRead then Exit;
    
      // initialize the status UI as needed...
      //
      // If TIdHTTP is running in the main thread, update your UI
      // components directly as needed and then call the Form's
      // Update() method to perform a repaint, or Application.ProcessMessages()
      // to process other UI operations, like button presses (for
      // cancelling the download, for instance).
      //
      // If TIdHTTP is running in a worker thread, use the TIdNotify
      // or TIdSync class to update the UI components as needed, and
      // let the OS dispatch repaints and other messages normally...
    
      TotalBytes := AWorkCountMax;
      LastWorkCount := 0;
      LastTicks := Ticks;
    end;
    
    procedure TSomeClass.HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
    var
      PercentDone: Integer;
      ElapsedMS: LongWord;
      BytesTransferred: Int64;
      BytesPerSec: Int64;
    begin
      if AWorkMode <> wmRead then Exit;
    
      ElapsedMS := GetTickDiff(LastTicks, Ticks);
      if ElapsedMS = 0 then ElapsedMS := 1; // avoid EDivByZero error
    
      if TotalBytes > 0 then
        PercentDone := (Double(AWorkCount) / TotalBytes) * 100.0;
      else
        PercentDone := 0.0;
    
      BytesTransferred := AWorkCount - LastWorkCount;
    
      // using just BytesTransferred and ElapsedMS, you can calculate
      // all kinds of speed stats - b/kb/mb/gm per sec/min/hr/day ...
      BytesPerSec := (Double(BytesTransferred) * 1000) / ElapsedMS;
    
      // update the status UI as needed...
    
      LastWorkCount := AWorkCount;
      LastTicks := Ticks;
    end;
    
    procedure TSomeClass.HttpWorkEnd(ASender: TObject; AWorkMode: TWorkMode);
    begin
      if AWorkMode <> wmRead then Exit;
    
      // finalize the status UI as needed...
    end;
    
     : 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;