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 withRETR
command.
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