我想要实现使用TIdHttp(Indy10)一个简单的HTTP下载。 我发现2种从互联网的代码示例。 遗憾的是他们没有满足我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请求到服务器。 我不知道如果某些服务器(如互联星空)可能会限制在特定时间段内的请求数。
我的期望
- 下载器应只发送一个GET请求到服务器。
- 下载开始时,不得分配的磁盘空间。
任何提示赞赏。
变体#1是simpliest,并印是如何打算使用。
关于磁盘分配问题,可以从派生一个新类TFileStream
并覆盖其SetSize()
方法什么也不做。 TIdHTTP
仍将尝试在适当的时候预分配的文件,但它实际上并不会分配任何磁盘空间。 写TFileStream
将根据需要增加文件。
关于状态报告, TIdHTTP
有OnWork...
用于这一目的的事件。 所述AWorkCountMax
所述的参数OnWorkBegin
将是如果不知道,实际文件大小如果已知的(响应不是分块),或0。 该AWorkCount
的参数OnWork
活动将是到目前为止传输的字节累计数。 如果文件大小是已知的,则可以显示通过简单地将总百分比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;
下面是说明如何使用OnWork显示进度条的组件的例子:
下载从互联网文件编程使用Delphi和印第安纳波利斯的进展情况
你不应该担心磁盘分配。 被分配实际上并没有写入,所以它不会破坏你的磁盘的磁盘空间。 高兴的是,它被分配使得它不可能是另一个进程声称磁盘空间,让你运行的空间!
不要忘了添加此为变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;