Jump to content
MrCamarium

Transfer a file from client to server (Delphi XE)

Recommended Posts

Customer side:

procedure TForm1.Button1Click(Sender: TObject);
begin
try
  Label1.Caption := 'Connessione in corso...';
  client.Port:=2630;
  client.Host:='127.0.0.1';
  client.Connect();
  except
  Label1.Caption := 'Errore Di Connessione';
end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  client.Disconnect;
  Label1.Caption := 'Disconnesso';
end;

procedure TForm1.Button3Click(Sender: TObject);
var TFLFileOut: TFileStream;
begin
  TFLFileOut:= TFileStream.Create(ToDownload.Text, fmOpenRead);
  Client.IOHandler.Write(TFLFileOut, 0, true);
end;

procedure TForm1.ClientConnect(Sender: TObject);
begin
Label1.Caption := 'Connesso';
Button1.Enabled := False;
Button2.Enabled := True;
end;

procedure TForm1.ClientDisconnect(Sender: TObject);
begin
Label1.Caption := 'Disconnesso';
Button1.Enabled := True;
Button2.Enabled := False;
end;

Server side:

procedure TForm1.ServerExecute(AContext: TIdContext);
var TFSFileIn: TFileStream;
begin
Label1.Caption := 'Arriva qualcosa...';
TFSFileIn:= TFileStream.Create(Edit1.Text, fmCreate);
AContext.Connection.IOHandler.ReadStream(TFSFileIn);
TFSFileIn.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
server.DefaultPort:=2630;
server.Active:=true;
end;

The program compiles correctly but the transferred file is zero.

Da Cliente A Server.zip

Share this post


Link to post
2 hours ago, MrCamarium said:

 


procedure TForm1.Button3Click(Sender: TObject);
var TFLFileOut: TFileStream;
begin
  TFLFileOut:= TFileStream.Create(ToDownload.Text, fmOpenRead);
  Client.IOHandler.Write(TFLFileOut, 0, true);
end;

 

You are leaking the TFileStream object.  You need to Free() it when you are done using it.  You should wrap the call to Write() in a try..finally block for that, in case Write() raises an exception, eg:

procedure TForm1.Button3Click(Sender: TObject);
var TFLFileOut: TFileStream;
begin
  TFLFileOut := TFileStream.Create(ToDownload.Text, fmOpenRead or fmShareDenyWrite);
  try
    Client.IOHandler.Write(TFLFileOut, 0, true);
  finally
    TFLFileOut.Free;
  end;
end;
Quote

 


procedure TForm1.ServerExecute(AContext: TIdContext);
var TFSFileIn: TFileStream;
begin
Label1.Caption := 'Arriva qualcosa...';
TFSFileIn:= TFileStream.Create(Edit1.Text, fmCreate);
AContext.Connection.IOHandler.ReadStream(TFSFileIn);
TFSFileIn.Free;
end;

Likewise, you should use try..finally here too, to ensure the TFileStream is Free()'d even if an exception is raised.

procedure TForm1.ServerExecute(AContext: TIdContext);
  var TFSFileIn: TFileStream;
begin
  Label1.Caption := 'Arriva qualcosa...';
  TFSFileIn := TFileStream.Create(Edit1.Text, fmCreate);
  try
    AContext.Connection.IOHandler.ReadStream(TFSFileIn);
  finally
    TFSFileIn.Free;
  end;
end;

But, more importantly, TIdTCPServer is a multi-threaded component.  Its OnExecute event is fired in the context of a worker thread, not the main UI thread. As such, accessing UI controls, such as your TLabel an TEdit controls, is NOT thread-safe.  You MUST synchronize with the main UI thread when accessing them, eg:

procedure TForm1.ServerExecute(AContext: TIdContext);
var
  FileName: string;
  TFSFileIn: TFileStream;
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Label1.Caption := 'Arriva qualcosa...';
      Filename := Edit1.Text;
    end
  end;
  TFSFileIn := TFileStream.Create(FileName, fmCreate);
  try
    AContext.Connection.IOHandler.ReadStream(TFSFileIn);
  finally
    TFSFileIn.Free;
  end;
end;
Quote

The program compiles correctly but the transferred file is zero.

Your calls to Write() and ReadStream() are matched up properly, so I cannot see a reason why the received file would be empty, provided that Write() and ReadStream() are actually being called.  Did you verify that with your debugger?

Edited by Remy Lebeau

Share this post


Link to post

Thank you for the answer, The code:

procedure TForm1.ServerExecute(AContext: TIdContext);
var
  FileName: string;
  TFSFileIn: TFileStream;
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Label1.Caption := 'Arriva qualcosa...';
      Filename := Edit1.Text;
    end
  end;
  TFSFileIn := TFileStream.Create(FileName, fmCreate);
  try
    AContext.Connection.IOHandler.ReadStream(TFSFileIn);
  finally
    TFSFileIn.Free;
  end;
end;

From errors in the compilation, however, even making the changes you suggested I still have a zero file. I am also attaching the file.

1.zip

Share this post


Link to post
9 hours ago, MrCamarium said:

The code:

...

From errors in the compilation

What errors, exactly? Please be more specific.  And, are you really using Delphi XE?  That is 12 years old.  Are you, at least, using an up-to-date version of Indy?  Or, are you are using a 12-year-old version?

Quote

however, even making the changes you suggested I still have a zero file.

The code to transfer the file looks fine to me, so you are just going to have to debug deeper.

Quote

I am also attaching the file.

1.zip

That zip file contains the same client code you originally posted.  It does not include the changes I suggested for the client.

Edited by Remy Lebeau

Share this post


Link to post
9 hours ago, MrCamarium said:

TThread.Synchronize(nil, procedure begin Label1.Caption := 'Arriva qualcosa...'; Filename := Edit1.Text; end end;

in above quoted, replace last end with )

that's all i see..

 

should note, i do believe serverexecute will be called multiple times until all data is received, depending on your file size it may be chopped up into smaller chunks, usually like 1500 byte or so, depends on routers and os configs.

 

Edited by qubits

Share this post


Link to post
2 hours ago, Remy Lebeau said:

What errors, exactly? Please be more specific.  And, are you really using Delphi XE?  That is 12 years old.  Are you, at least, using an up-to-date version of Indy?  Or, are you are using a 12-year-old version?

The code to transfer the file looks fine to me, so you are just going to have to debug deeper.

That zip file contains the same client code you originally posted.  It does not include the changes I suggested for the client.

I am using Embarcadero® RAD Studio XE Version 15.0.3890.34076 the version of Indy is 10.5.7 the code:

procedure TForm1.ServerExecute(AContext: TIdContext);
var
  FileName: string;
  TFSFileIn: TFileStream;
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Label1.Caption := 'Arriva qualcosa...';
      Filename := Edit1.Text;
    end
  end;
  TFSFileIn := TFileStream.Create(FileName, fmCreate);
  try
    AContext.Connection.IOHandler.ReadStream(TFSFileIn);
  finally
    TFSFileIn.Free;
  end;
end;

Da errore: [DCC Error] Unit1.pas(46): E2029 ')' expected but 'END' found

Correcting it like this:

procedure TForm1.ServerExecute(AContext: TIdContext);
var
  FileName: string;
  TFSFileIn: TFileStream;
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Label1.Caption := 'Arriva qualcosa...';
      Filename := Edit1.Text;
    end;
  end;
  TFSFileIn := TFileStream.Create(FileName, fmCreate);
  try
    AContext.Connection.IOHandler.ReadStream(TFSFileIn);
  finally
    TFSFileIn.Free;
  end;
end;

It gives me an error: [DCC Error] Unit1.pas(45): E2029 ')' expected but ';' found

1.zip

Share this post


Link to post

there's an open bracket on Synchronize

 

Should be

procedure TForm1.ServerExecute(AContext: TIdContext);
var
  FileName: string;
  TFSFileIn: TFileStream;
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Label1.Caption := 'Arriva qualcosa...';
      Filename := Edit1.Text;
    end
  );
  TFSFileIn := TFileStream.Create(FileName, fmCreate);
  try
    AContext.Connection.IOHandler.ReadStream(TFSFileIn);
  finally
    TFSFileIn.Free;
  end;
end;

 

Indy's got nice FTP components, might be easier solution..

Edited by qubits

Share this post


Link to post
7 hours ago, qubits said:

in above quoted, replace last end with )

OK, thanks.

Quote

should note, i do believe serverexecute will be called multiple times until all data is received

The TIdTCPServer.OnExecute event is fired in an endless loop for the lifetime of the TCP connection.  The event doesn't care what data is transmitted.  That is the responsibility of the code inside of the event to deal with as needed.

Quote

depending on your file size it may be chopped up into smaller chunks, usually like 1500 byte or so, depends on routers and os configs.

That doesn't really matter in this case.  ReadStream() will handle those chunks internally.  The code's use of Write() is sending the file size followed by the file data.  Then the code's use of ReadStream() is reading the file size and then reading the file data up to that size.

Edited by Remy Lebeau

Share this post


Link to post
4 hours ago, MrCamarium said:

the version of Indy is 10.5.7

That is an extremely old version of Indy, you should upgrade to the latest version, which is 10.6.2.  Even if you can't upgrade your IDE.

Edited by Remy Lebeau

Share this post


Link to post
6 hours ago, Remy Lebeau said:

OK, thanks.

The TIdTCPServer.OnExecute event is fired in an endless loop for the lifetime of the TCP connection.  The event doesn't care what data is transmitted.  That is the responsibility of the code inside of the event to deal with as needed.

That doesn't really matter in this case.  ReadStream() will handle those chunks internally.  The code's use of Write() is sending the file size followed by the file data.  Then the code's use of ReadStream() is reading the file size and then reading the file data up to that size.

The test file is about 200K being local 127.0.0.1 should be fast transfer. At this point how do I know that the transfer is taking place? If I wanted to use a TProgressBar what should I do?

Share this post


Link to post
6 hours ago, Remy Lebeau said:

That is an extremely old version of Indy, you should upgrade to the latest version, which is 10.6.2.  Even if you can't upgrade your IDE.

I downloaded the libraries but couldn't find a guide that tells me how to update the components. Inside the Lib folder there is a * .Bat file corresponding to each version of Delphi. I launched Fullc_XE2.bat but when I reopen my IDE I find the same version of Indy 10.5.7.

Share this post


Link to post
On 3/5/2022 at 1:34 AM, Remy Lebeau said:

OK, thanks.

The TIdTCPServer.OnExecute event is fired in an endless loop for the lifetime of the TCP connection.  The event doesn't care what data is transmitted.  That is the responsibility of the code inside of the event to deal with as needed.

That doesn't really matter in this case.  ReadStream() will handle those chunks internally.  The code's use of Write() is sending the file size followed by the file data.  Then the code's use of ReadStream() is reading the file size and then reading the file data up to that size.

I figured out where I was wrong, the code works fine. In the server program as destination I did not give him the folder but the name of the file. Thank you! However, if you can suggest me a method with which I can monitor the sending I would be grateful.

Share this post


Link to post
On 3/4/2022 at 10:38 PM, MrCamarium said:

The test file is about 200K being local 127.0.0.1 should be fast transfer. At this point how do I know that the transfer is taking place? If I wanted to use a TProgressBar what should I do?

You can use the connection's OnWork events for that purpose.  Just be aware that they are fired in the context of the thread that is performing the transfer, so if that is not the main UI thread then you will have to synchronize your UI updates with the main thread.

On 3/4/2022 at 10:42 PM, MrCamarium said:

I downloaded the libraries but couldn't find a guide that tells me how to update the components.

https://www.indyproject.org/2021/02/10/links-to-old-indy-website-pages-are-currently-broken/

https://web.archive.org/web/20200925081341/http://ww2.indyproject.org/Sockets/Docs/Indy10Installation.EN.aspx

On 3/4/2022 at 10:42 PM, MrCamarium said:

Inside the Lib folder there is a * .Bat file corresponding to each version of Delphi. I launched Fullc_XE2.bat

That is a build script for C++Builder (hence the C in the filename), not for Delphi.  Indy stopped using build scripts for Delphi after D2009.  For Delphi, you can just open the project files directly in the IDE and use the Compile and Install options in the Project Manager.

On 3/4/2022 at 10:42 PM, MrCamarium said:

but when I reopen my IDE I find the same version of Indy 10.5.7.

Of course, because 1) you didn't remove the old version from the IDE, and 2) the build script only compiles the library binaries, but it does not install them into the IDE.  You have to do that step manually.

On 3/5/2022 at 11:06 PM, MrCamarium said:

I figured out where I was wrong, the code works fine. In the server program as destination I did not give him the folder but the name of the file. Thank you!

Never use relative paths, always use absolute paths.

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×