我想使用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)是否可能限制特定时间段内的请求数量.
我的期望
>下载程序只能向服务器发送一个GET请求.
>下载开始时不能分配磁盘空间.
任何提示都不胜感激.
解决方法
变式#1是最简单的,而且Indy是如何被使用的.
关于磁盘分配问题,您可以从TFileStream派生一个新类,并覆盖其SetSize()方法,不执行任何操作. TIdHTTP仍然会尝试在适当的时候预先分配文件,但实际上并不会分配任何磁盘空间.写入TFileStream将根据需要增长文件.
关于状态报告,TIdHTTP具有用于此目的的OnWork …事件. OnWorkBegin的AWorkCountMax参数将是实际的文件大小(如果已知的响应未被分块),如果不知道则为0. OnWork事件的AWorkCount参数将是到目前为止传输的累计字节数.如果文件大小已知,您可以通过简单地将AWorkCount除以AWorkCountMax乘以100来显示总百分比,否则只显示AWorkCount值.如果要显示传输速度,可以根据AWorkCount值和多个OnWork事件之间的时间间隔来计算.
尝试这个:
type TNoPresizeFileStream = class(TFileStream) procedure procedure SetSize(const NewSize: Int64); override; end; procedure TNoPresizeFileStream.SetSize(const NewSize: Int64); begin end;
.
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;