Upload file to FTP Server using Indy

16,252

I have struggled a bit with Indy Ftp over the years. At some point I turned to an alternative (free) Ftp client from OverbyteIcs (click ICS and then click Download ICS-V8.16 (Apr, 2015)). If you are not against using a freeware package, the following code will do the job:

uses
  ...
  OverbyteIcsFtpCli;

procedure FtpUploadFile( 
                             HostName: String; 
                             UserName: String; 
                             Password: String; 
                             UploadFileName: String; 
                             ToHostDir : String );
var
  FTP: TFtpClient;
begin
  FTP := TFtpClient.Create(nil);
  try
    FTP.HostName := HostName;
    FTP.Passive := True;
    FTP.Binary := True;
    FTP.Username := UserName;
    FTP.Password := Password;
    FTP.Port := '21';

    if not FTP.Open then
      raise Exception.Create('Failed to connect: ' + FTP.ErrorMessage);

    if (not FTP.User) or (not FTP.Pass) then 
      raise Exception.Create('Failed to login: ' + FTP.ErrorMessage);

    FTP.HostDirName := ToHostDir;
    if not FTP.Cwd then
      raise Exception.Create('Failed to change dir: ' + FTP.ErrorMessage);

    FTP.LocalFileName := UploadFileName;
    FTP.HostFileName := ExtractFileName(UploadFileName);

    if not FTP.Put then
      raise Exception.Create('Failed to upload file: ' + FTP.ErrorMessage);
  finally
    FTP.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   FtpUploadFile('rubilaxe.hostoi.com',  
                     '******', '******',
                     IncludeTrailingPathDelimiter( 
                          ExtractFilePath(Application.ExeName) ) +'datafile.zip',
                     '/files'  );
end;
Share:
16,252
TheGreenOmega
Author by

TheGreenOmega

Updated on June 04, 2022

Comments

  • TheGreenOmega
    TheGreenOmega almost 2 years

    I am unable to upload a file to my 000webhost.com FTP Server using Indy for Lazarus. I've tested the ftp connectivity with Windows Command Prompt, and it works fine. These are my Settings (IdFTP):

    IdFTP1.Host:='shabala.com';
    IdFTP1.Passive:=True;
    IdFTP1.TransferType:=ftBinary;
    IdFTP1.Username:='******';
    IdFTP1.Password:='******';
    IdFTP1.Port:=21;
    

    And this is the code which I use to call my TIdFTP component, IdFTP1:

    IdFTP1.Connect(True);
    //IdFTP1.ChangeDir('/Sessions');
    IdFTP1.Put(GetCurrentDir+'\'+Token+'.cmd',Token+'.cmd', False);
    IdFTP1.Quit;
    IdFTP1.Disconnect;
    

    where the variable Token is declared as:

    Token: String; 
    

    When I ran the program for the first time, it kept freezing and I declared a TIdAntiFreeze component to prevent it from freezing. So, this is what happens now: sometimes the program works fine, but no files are transferred to the server (If I try to repeat the upload, it gives me an EIdAlredyConnected error), and sometimes (if I change the code a bit, nothing extra) it gives me an EIdProtocolReplyError with a strange message. I tried to catch the exception and get my program to display the message, and I've got some strange characters:

    $ £ ï túÁÕÖ îÖõ)€¶K…ÅõÞl%ÇðåÀ¨Á“§pp
    A¨%˜ßï7!ƒDªÉ[…oˆ_£P*¡°z1K¢H€Î¨ERPö/
    üð΃ç±ïpļƒÏƒ‹Ò1ì
    ¿Á{»(g{å¥r…Ž¹öЭR_JúѯuBûŸ€Œ Pp6o¯c[JgžÎ¿­Èà¦Ä€VJþz’0è–`BO@T

    The response looks like this if formatted correctly:

    Strange response

    I couldn't put the formatted text here directly.

    The server works absolutely fine, the directories I'm trying to upload to are chmodded to 777, and I've discovered that the file's size (which I want to upload) isn't greater than 3 KBs.

    Any ideas?

    • Jerry Dodge
      Jerry Dodge over 8 years
      By "bugging" I assume you mean "freezing" - since you attempted to use TIdAntiFreeze to solve it?
    • TheGreenOmega
      TheGreenOmega over 8 years
      Yes. The button which the procedure is assigned to remained in "Clicked State". It is a TBCButton (package BGRAControls). Also, the application wasn't responding (If I clicked on the form, it crashed). @JerryDodge
    • Remy Lebeau
      Remy Lebeau over 8 years
      That is because TIdFTP blocks the calling thread while it is busy doing work (as do all of Indy's components). If the calling thread is the main UI thread, you prevent it from processing new messages in a timely manner, unless you use TIdAntiFreeze. Better to move the TIdFTP to a worker thread instead.
    • TheGreenOmega
      TheGreenOmega over 8 years
      @RemyLebeau The graphics don't matter. TIdAntiFreeze works just fine. That's not my problem. My problem is that I still can't get the upload to work. Moving the TIdFTP component to another thread won't help... And I still have no idea what caused those strange responses. Anyways, thanks for the suggestion.
    • Remy Lebeau
      Remy Lebeau over 8 years
      Don't use GetCurrentDir(), it relies on the calling process's current working directory, which changes value during the process's lifetime. If the local file is in your app's folder, use ExtractFilePath(Application.ExeName) instead. In any case, what you have shown is not an FTP response, and certainly should never appear in an EIdProtocolReplyError exception, which makes me wonder if you are actually connected to a real FTP server and not something else.
    • Remy Lebeau
      Remy Lebeau over 8 years
      And the only way you can get EIdAlreadyConnected is if Connected() returns true when calling Connect(). Connected() returns true if TIdFTP is actually connected to a server, or if disconnected but there is still unread data in the IOHandler.InputBuffer from an earlier connection. Don't call Quit(), it is deprecated, just call Disconnect() by itself. And if you call Disconnect() in reply to a raised exception, clear the InputBuffer of unread data: IdFTP1.Disconnect; if Assigned(IdFTP1.IOHandler) then IdFTP1.IOHandler.InputBuffer.Clear;
    • TheGreenOmega
      TheGreenOmega over 8 years
      @RemyLebeau If I'm not connected to the FTP server, then how can I get the EIdAlredyConnected exception? There's no leftover in the InputBuffer, I checked it. My greatest problem is that I can't finish the upload procedure. To check where the program hangs on, I created and assigned an OnWorkEnd event with the ShowMessage('Upload done!'); code inside. The OnWorkEvent is never executed.
    • Remy Lebeau
      Remy Lebeau over 8 years
      @TheGreenOmega: The only way for EIdAlreadyConnected to be raised is if Connected() returns true when Connect() is called (Connect() checks Connected() internally), and the only way for Connected() to return true is if the socket is physically connected to a server (or at least a disconnect has not been detected yet) or the IOHandler is still open and its InputBuffer has unread data. If OnWorkBegin is triggered, OnWorkEnd is guaranteed to be triggered, unless a deadlock is occurring in between the two events.
    • TheGreenOmega
      TheGreenOmega over 8 years
      @RemyLebeau Let's consider that the connection is active. I still can't upload files. What am I doing wrong?
    • Remy Lebeau
      Remy Lebeau over 8 years
      The only way to get EIdAlreadyConnected in the code you showed is if you call Connect(), then Put() or Quit() raises an exception bypassing Disconnect(), then you call Connect() again. As for the EIdProtocolReplyError, I can't answer that without seeing the original server response that triggered it.