I'm developing a FTP Server with Delphi XE 6 and Indy10. The problem is that i need to limit the speed of download (must be configurable Ex. 1 KB/s, 1 MB/s, etc.) and i don't make it work. I know some props like BitsPerSec, etc. but this only affect the protocol data exchange, not the file exchange with RETR command. I see in IdFTPServer.pass, and the Stream is converted to string and send with a repeat/until loop (with IOHandler.Write()) but i need some form of control the upload/download process and be able to limit the speed for all incoming client connections. Some help to achieve this please?.
PD: Sorry for my poor english.
I try to implement a CommandHandler for the RETR command with this code:
procedure TForm1.IdFTPServer1CommandHandlers0Command(ASender: TIdCommand);
var
LContext : TIdFTPServerContext;
vStream: TFileStream;
path: String;
vX: Integer;
LEncoding: IIdTextEncoding;
begin
LEncoding := IndyTextEncoding_8Bit;
LContext := ASender.Context as TIdFTPServerContext;
path := 'D:\indy_in_depth.pdf';
try
vStream := TFileStream.Create(path, fmOpenRead);
//LContext.DataChannel.FtpOperation := ftpRetr;
//LContext.DataChannel.Data := VStream;
LContext.DataChannel.OKReply.SetReply(226, RSFTPDataConnClosed);
LContext.DataChannel.ErrorReply.SetReply(426, RSFTPDataConnClosedAbnormally);
ASender.Reply.SetReply(150, RSFTPDataConnToOpen);
ASender.SendReply;
Memo1.Lines.Add('Sending a file with: ' + IntToStr(vStream.Size) + ' bytes');
//Make control of speed here !
for vX := 1 to vStream.Size do
begin
vStream.Position := vX;
LContext.Connection.IOHandler.Write(vStream, 1, False);
if vX mod 10000 = 0 then
Memo1.Lines.Add('Sended byte: ' + IntToStr(vX));
end;
//LContext.DataChannel.InitOperation(False);
//LContext.Connection.IOHandler.Write(vStream);
//LContext.KillDataChannel;
LContext.Connection.Socket.Close;
VStream.Free;
except
on E: Exception do
begin
ASender.Reply.SetReply(550, E.Message);
end;
end;
end;
But the Filezilla FTP Client show stream data like part of other command.
Filezilla Client
The problem is that i need to limit the speed of download (must be configurable Ex. 1 KB/s, 1 MB/s, etc.) and i don't make it work. I know some props like
BitsPerSec, etc. but this only affect the protocol data exchange, not the file exchange withRETRcommand.
BitsPerSec is a property of the TIdInterceptThrottler class (along with RecvBitsPerSec and SendBitsPerSec properties). An instance of that class can be assigned to any TIdTCPConnection object via its Intercept property. This is not limited to just the command connection, it can be used for transfer connections, too.
You can access the TIdTCPConnection object of a file transfer in TIdFTPServer via the TIdFTPServerContext.DataChannel.FDataChannel member, such as in the OnRetrieveFile event:
type
// the TIdDataChannel.FDataChannel member is 'protected',
// so use an accessor class to reach it...
TIdDataChannelAccess = class(TIdDataChannel)
end;
procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerContext;
const AFileName: TIdFTPFileName; var VStream: TStream);
var
Conn: TIdTCPConnection;
begin
Conn := TIdDataChannelAccess(ASender.DataChannel).FDataChannel;
if Conn.Intercept = nil then
Conn.Intercept := TIdInterceptThrottler.Create(Conn);
TIdInterceptThrottler(Conn.Intercept).BitsPerSec := ...;
VStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
end;
An alternative is to create your own T(File)Stream derived class to override the virtual Read() and Write() methods to perform your own throttling as needed, and then assign an instance of that class to the VStream parameter of the OnRetrieveFile and OnStoreFile events.
I see in
IdFTPServer.pas, and the Stream is converted to string and send with a repeat/until loop (with IOHandler.Write())
No, a stream is not converted to a string. You are misreading the code. A stream's content is sent as-is as binary data in a single call to the IOHandler.Write(TStream) method.
but i need some form of control the upload/download process and be able to limit the speed for all incoming client connections.
See above.
I try to implement a CommandHandler for the RETR command
You should NOT be handling the RETR command directly. TIdFTPServer already does that for you. You are meant to use the OnRetrieveFile event instead to prepare downloads, and the OnStoreFile event for uploads.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With