【问题标题】:Correctly making a threaded FTP download (Indy/Android)正确进行线程 FTP 下载(Indy/Android)
【发布时间】:2018-03-17 19:47:03
【问题描述】:

我已经创建了一个 IdFTP 来从我的 FTP 服务器下载文件,但是当我尝试将其设置为 Threaded 时,它卡在了 Android 操作系统中的“正在解析主机名...”中。

没有线程(工作正常):

uses ..., IdFTPCommon;

var
  RecordDownload: TMemoryStream;

uses System.IOUtils;

procedure TForm1.Button1Click(Sender: TObject);
begin
  IdFTP1.Host := 'motoristaajudante.ddns.net';
  IdFTP1.Port := 2121;
  IdFTP1.DataPortMin := 50100;
  IdFTP1.DataPortMax := 51100;
  IdFTP1.Username := 'anonymous';
  IdFTP1.TransferType := IdFTPCommon.TIdFTPTransferType.ftBinary;
  IdFTP1.Passive := True;
  try
    IdFTP1.Connect();
    IdFTP1.Get('00001.m4a',TPath.GetDocumentsPath + PathDelim + '00001.m4a',True,False);
  except
    IdFTP1.Disconnect;
  end;
end;

procedure TForm1.IdFTP1AfterGet(ASender: TObject; AStream: TStream);
begin
  IdFTP1.Disconnect;
end;

procedure TForm1.IdFTP1WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  if FileExists(TPath.GetDocumentsPath + PathDelim + '00001.m4a') then
  begin
      ShowMessage('Downloaded!');
  end;
end;

还有我在this solution 之后编写的线程代码:

uses ..., IdFTPCommon;

type
  TLoadThread = class(TThread)
  public
    constructor Create; reintroduce;
  protected
    procedure Execute; override;
  end;

type
  TForm1 = class(TForm)
  ...
  procedure ThreadTerminated(Sender: TObject);

var
  RecordDownload: TMemoryStream;
  Loading: Boolean = False;
  zLThread: TLoadThread = nil;

uses System.IOUtils;

constructor TLoadThread.Create;
begin
  inherited Create(True);
  FreeOnTerminate := True;
end;

procedure TLoadThread.Execute;
begin
try
  Form1.IdFTP1.Connect();
  Form1.IdFTP1.Get('00001.m4a',TPath.GetDocumentsPath + PathDelim + '00001.m4a',True,False);
except
  Form1.IdFTP1.Disconnect;
end;
end;

procedure TForm1.ThreadTerminated(Sender: TObject);
begin
  zLThread := nil;
  Loading := False;
  FloatAnimation1.Enabled := False;
  FloatAnimation2.Enabled := False;
  Arc3.StartAngle := -90;
  Arc3.EndAngle := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  IdFTP1.Host := 'motoristaajudante.ddns.net';
  IdFTP1.Port := 2121;
  IdFTP1.DataPortMin := 50100;
  IdFTP1.DataPortMax := 51100;
  IdFTP1.Username := 'anonymous';
  IdFTP1.TransferType := IdFTPCommon.TIdFTPTransferType.ftBinary;
  IdFTP1.Passive := True;
  zLThread := TLoadThread.Create;
  zLThread.OnTerminate := ThreadTerminated;
  zLThread.Start;
  Loading := True;
  FloatAnimation1.Enabled := True;
  FloatAnimation2.Enabled := True;
end;

procedure TForm1.IdFTP1AfterGet(ASender: TObject; AStream: TStream);
begin
  IdFTP1.Disconnect;
end;

procedure TForm1.IdFTP1WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  Form1.FloatAnimation1.Enabled := False;
  Form1.FloatAnimation2.Enabled := False;
  Form1.Arc3.StartAngle := -90;
  Form1.Arc3.EndAngle := 0;
  if FileExists(TPath.GetDocumentsPath + PathDelim + '00001.m4a') then
  begin
      ShowMessage('Downloaded!');
  end;
end;

procedure TForm1.IdFTP1Status(ASender: TObject; const AStatus: TIdStatus;
  const AStatusText: string);
begin
  Memo1.Lines.Add(AStatusText);
  Application.ProcessMessages;
end;

然后 FTP 状态显示它卡在“正在解析主机名 ...”。如何使其正确穿线?

【问题讨论】:

    标签: android delphi ftp indy delphi-10.1-berlin


    【解决方案1】:

    没有理由像使用它们一样使用OnAfterGetOnWorkEnd 事件。 Indy 是同步的。 TIdFTP.Get() 在传输完成之前不会返回,如果发生错误,则会引发异常。

    因此,摆脱这两个事件处理程序,使用try/finally 而不是try/except 调用Disconnect(),并且仅在Get() 未引发时才处理下载。

    OnWorkEndOnStatus 事件在调用 Connect()Disconnect()Get() 的同一线程的上下文中触发。因此,在您的线程示例中,这将是工作线程,而不是主线程。但是您的事件处理程序不会同步对您的 UI 控件的访问。这可能会导致各种问题,包括您遇到的冻结。您必须同步(TThread.OnTerminated 事件已同步)。

    话虽如此,试试这个:

    非线程:

    uses
      ..., IdFTPCommon;
    
    ...
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      IdFTP1.Host := 'motoristaajudante.ddns.net';
      IdFTP1.Port := 2121;
      IdFTP1.DataPortMin := 50100;
      IdFTP1.DataPortMax := 51100;
      IdFTP1.Username := 'anonymous';
      IdFTP1.TransferType := IdFTPCommon.TIdFTPTransferType.ftBinary;
      IdFTP1.Passive := True;
      try
        IdFTP1.Connect;
        try
          IdFTP1.Get('00001.m4a', TPath.GetDocumentsPath + PathDelim + '00001.m4a', True, False);
        finally
          IdFTP1.Disconnect;
        end;
        ShowMessage('Downloaded!');
      except
        ShowMessage('Error while downloading!');
      end;
    end;
    

    线程:

    uses
      ..., IdFTPCommon;
    
    type
      TLoadThread = class(TThread)
      public
        constructor Create; reintroduce;
      protected
        procedure Execute; override;
      end;
    
    type
      TForm1 = class(TForm)
        ...
        IdFTP1: TIdFTP;
        procedure ThreadTerminated(Sender: TObject);
        ... 
      private
        Loading: Boolean;
        zLThread: TLoadThread;
      end;
    
    ...
    
    constructor TLoadThread.Create;
    begin
      inherited Create(True);
      FreeOnTerminate := True;
    end;
    
    procedure TLoadThread.Execute;
    begin
      Form1.IdFTP1.Connect;
      try
        Form1.IdFTP1.Get('00001.m4a', TPath.GetDocumentsPath + PathDelim + '00001.m4a', True, False);
      finally
        Form1.IdFTP1.Disconnect;
      end;
    end;
    
    procedure TForm1.ThreadTerminated(Sender: TObject);
    begin
      zLThread := nil;
      Loading := False;
      FloatAnimation1.Enabled := False;
      FloatAnimation2.Enabled := False;
      Arc3.StartAngle := -90;
      Arc3.EndAngle := 0;
      If TThread(Sender).FatalException = nil then
        ShowMessage('Downloaded!')
      else
        ShowMessage('Error while Downloading!');
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      IdFTP1.Host := 'motoristaajudante.ddns.net';
      IdFTP1.Port := 2121;
      IdFTP1.DataPortMin := 50100;
      IdFTP1.DataPortMax := 51100;
      IdFTP1.Username := 'anonymous';
      IdFTP1.TransferType := IdFTPCommon.TIdFTPTransferType.ftBinary;
      IdFTP1.Passive := True;
      zLThread := TLoadThread.Create;
      zLThread.OnTerminate := ThreadTerminated;
      zLThread.Start;
      Loading := True;
      FloatAnimation1.Enabled := True;
      FloatAnimation2.Enabled := True;
    end;
    
    procedure TForm1.IdFTP1Status(ASender: TObject; const AStatus: TIdStatus; const AStatusText: string);
    begin
      TThread.Queue(nil, 
        procedure
        begin
          Memo1.Lines.Add(AStatusText);
        end
      );
    end;
    

    【讨论】:

    • 谢谢 Remy,它运行良好,并且给了我一种处理下载错误的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 2017-12-29
    相关资源
    最近更新 更多