14 декабря 2020

Clever Internet Suite. Передача файлов по SFTP

    SFTP (SSH File Transfer Protocol) - протокол прикладного уровня, предназначенный для выполнения различных операций с файлами через безопасное соединение. В отличие от FTP, он шифрует команды и данные, предотвращая открытую передачу конфиденциальной информации по сети. Давайте посмотрим, как из программы на Delphi загрузить файл на SFTP-сервер и скачать его.
    Для работы с SFTP-сервером я воспользуюсь компонентой TclSFtp из библиотеки Clever Internet Suite. Она реализует поддержку протокола SSH FTP и позволяет через него работать с файлами и каталогами.
program sftp;

{$APPTYPE CONSOLE}

uses
  System.Classes, System.SysUtils, clSFtp, clSFtpHelper;

procedure Test(const sLocalFile, sFtpDir: String);
var
  sftp: TclSFtp;
  sFileName: String;
begin
  sFileName := ExtractFileName(sLocalFile);
  sftp := TclSFtp.Create(nil);
  try
    sftp.CharSet  := 'UTF-8';

    // подключение к SFTP-серверу
    sftp.Server   := 'localhost';
    sftp.Port     := 22;
    sftp.UserName := 'BlackCat';
    sftp.Password := '123';
    sftp.Open;

    if sftp.DirectoryExists(sFtpDir) then
      begin
        // выбираем каталог на SFTP-сервере
        sftp.ChangeCurrentDir(sFtpDir);

        // загружаем файл на SFTP-сервер
        sftp.PutFile(sLocalFile, sFileName);

        // загружаем файл с SFTP-сервера
        sftp.GetFile(sFileName, 'c:\temp\' + sFileName);
      end;
  finally
    sftp.Free
  end;
end;

begin
  try
    Test('d:\MSSQL\Backup\xyz.bak', 'backup');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.
Как видите – все очень просто. Небольшие пояснения и советы:
  • Процедура Test собрана из кусков моего класса и для упрощения очищена от обработки ошибок.
  • Строка 16. CharSet = 'UTF-8' позволит избежать проблем с национальными символами.
  • Строка 25. К сожалению, метод ChangeCurrentDir в 28-й строке не вызывает ошибку если на SFTP-сервере не существует нужный каталог. При этом у TclSFtp нет метода для проверки наличия каталога. Можно, конечно, использовать метод FileExists (он работает и для каталогов), но это может привести к ошибке, если пользователь укажет в качестве каталога имя файла. В таком случае, мне кажется, что будет более корректно сделать проверку отлавливая ошибку при вызове метода получения списка каталогов и файлов
    procedure GetList(AList: TStrings; const AFilePath: string = ''; ADetails: Boolean = True);
    Но мне это показалось не правильным, и я, чтобы добраться к приватным методам TclSFtp, сделал для него class helper с функцией DirectoryExists
    unit clSFtpHelper;
    
    interface
    
    uses
      System.SysUtils, clSFtp, clTranslator, clSFtpUtils;
    
    type
      TclSFtpHelper = class helper for TclSFtp
        function DirectoryExists(const AFilePath: string): Boolean;
      end;
    
    implementation
    
    { TclSFtpHelper }
    
    function TclSFtpHelper.DirectoryExists(const AFilePath: string): Boolean;
    begin
      if AFilePath.isEmpty
        then Result := True
        else with Self do
              try
                SendOPENDIR(TclTranslator.GetBytes(GetFullPath(AFilePath), 
                                                   CharSet));
                Result := (FResponseType = SSH_FXP_HANDLE) and 
                          (FStatusCode = 0);
              except
                Result := False;
              end;
    end;
    
    end.
  • Методы PutFile и GetFile могут работать с потоками и загружать файлы по частям, что позволяет реализовать проверку их сигнатур или докачку.
  • Используя событие OnVerifyServer
    TclVerifySshPeerEvent = procedure(Sender: TObject; const AHost, AKeyType, AFingerPrint, AHostKey: string; var AVerified: Boolean) of object;
    можно проверить информацию об открытом ключе SFTP-сервера.
  • Используя событие OnProgress
    TclProgressEvent = procedure(Sender: TObject; ABytesProceed, ATotalBytes: Int64) of object;
    можно следить за процессом загрузки файла.
  • Комбинируя событие OnDirectoryListing
    TclSFtpDirectoryListingEvent = procedure(Sender: TObject; const AFileName: string; AFileAttrs: TclSFtpFileAttrs) of object;
    и вызов методов DirectoryListing/GetList можно получить детальную информацию о каталогах и файлах.

Комментариев нет:

Отправка комментария