gpt4 book ai didi

delphi - 使用 TIdHttp 逐步下载文件

转载 作者:可可西里 更新时间:2023-11-01 15:06:04 29 4
gpt4 key购买 nike

我想使用 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 中显示下载进度,除非代码在后台线程中执行(或者我们可以绑定(bind) 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. 下载开始时不得分配磁盘空间。

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

最佳答案

变体 #1 是最简单的,也是 Indy 的用途。

关于磁盘分配问题,可以从TFileStream派生一个新的类,覆盖它的SetSize()方法什么也不做。 TIdHTTP 仍会在适当的时候尝试预分配文件,但实际上不会分配任何磁盘空间。写入 TFileStream 将根据需要增大文件。

关于状态报告,TIdHTTP 有用于该目的的OnWork... 事件。 OnWorkBeginAWorkCountMax 参数将是实际文件大小(如果已知)(响应未分块),如果未知则为 0。 OnWork 事件的 AWorkCount 参数将是迄今为止已传输的累计字节数。如果文件大小已知,您可以通过简单地将 AWorkCount 除以 AWorkCountMax 并乘以 100 来显示总百分比,否则只显示 AWorkCount本身的值(value)。如果要显示传输速度,可以根据 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;

关于delphi - 使用 TIdHttp 逐步下载文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13641055/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com