unit uSftpClient;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, ComCtrls, StdCtrls, SyncObjs, //
ScBridge, IdBaseComponent, IdComponent, IdIOHandler, IdIOHandlerSocket, ScIndy, ScSFTPClient, ScSFTPUtils, ScFunctions, TypInfo, ScTypes, ScSSHClient, ScSSHUtils, ScCLRClasses, ScUtils;
type
TSftpClient = class
sFtpClient : TScSFTPClient;
sIdHandler : TScIdIOHandler;
sShClient : TScSSHClient;
sMemoryStorage : TScMemoryStorage;
FTimeout : integer;
FVersion : string;
private
sRootDir : string;
procedure sshClientServerKeyValidate(Sender : TObject; NewServerKey : TScKey; var Accept : Boolean);
procedure SftpClientConnect(Sender : TObject);
procedure SftpClientDisconnect(Sender : TObject);
procedure SftpClientSuccess(Sender : TObject; Operation : TScSFTPOperation; const FileName : string; const Handle : TArray<Byte>;
const Message : string);
procedure SftpClientError(Sender : TObject; Operation : TScSFTPOperation; const FileName : string; const Handle : TArray<Byte>;
ErrorCode : integer; const ErrorMessage : string; var Fail : Boolean);
procedure SftpClientCreateLocalFile(Sender : TObject; const LocalFileName, RemoteFileName : string; Attrs : TScSFTPFileAttributes; var Handle :
{$IFDEF VER220} Cardinal {$ELSE} NativeUInt {$ENDIF}); overload; // Delphi XE 버전은 Cardinal, 아니면 NativeUInt
procedure SftpClientDirectoryList(Sender : TObject; const Path : string; const Handle : TBytes; FileInfo : TScSFTPFileInfo; Eof : Boolean);
procedure scmrystrg1CheckUserKey(Sender : TObject; ClientInfo : TScSSHClientInfo; Key : TScKey; var Accept : Boolean);
procedure scmrystrg1CheckUserPass(Sender : TObject; ClientInfo : TScSSHClientInfo; const Password : string; var Accept : Boolean);
public
slMsg : TStringList;
constructor Create(host : string; port : integer; id, pw : string); overload;
constructor Create(host, port, id, pw : string); overload;
destructor Destroy; override;
procedure Initialize; // SFTP 연결
procedure Finalize; // SFTP 연결 해제
function GetRootDir : string; // 루트 경로 읽기
function IsDirectoryExists(const sSRootDir, sDirectory : string) : Boolean;
procedure OpenDir(const Path : string; const SelectedName : string = ''); // 폴더 경로 읽어오기
procedure RemoveDir(const Path : string); // 디렉토리 삭제
procedure MakeDir(const DirName : string);
function GetDirName(const Path : string) : string;
function GetConnected : Boolean;
// 다운로드
function DownloadFile(sFilename, sTargetname : string; bOverWrite : Boolean = True) : Boolean; overload; // file -> file
function DownloadFile(sFilename : string; Target : TMemoryStream) : Boolean; overload; // file -> memorystream
// 업로드
function UploadFile(sFilename, sTargetname : string; bOverWrite : Boolean = True) : Boolean; overload; // file -> file
function UploadFile(src : TMemoryStream; sTargetname : string; bOverWrite : Boolean = True) : Boolean; overload; // memorystream -> file
procedure RenameFile(sOldName, sNewname : string; bOverWrite : Boolean = True);
procedure SetTimeOut(value : integer);
property Connected : Boolean read GetConnected;
property Timeout : integer read FTimeout write SetTimeOut default 3600;
property Version : string read FVersion write FVersion;
end;
// 한번만 실행할 경우 해당 클래스 이용
TSftpClientRun = class
// 다운로드
// file -> file
class function DownloadFile(host : string; port : integer; id, pw : string; sFilename, sTargetname : string; var msg : string;
bOverWrite : Boolean = True) : Boolean; overload;
// file -> memorystream
class function DownloadFile(host : string; port : integer; id, pw : string; sFilename : string; Target : TMemoryStream; var msg : string)
: Boolean; overload;
// file -> file
class function DownloadFile(host, port, id, pw : string; sFilename, sTargetname : string; var msg : string; bOverWrite : Boolean = True)
: Boolean; overload;
// file -> memorystream
class function DownloadFile(host, port, id, pw : string; sFilename : string; Target : TMemoryStream; var msg : string) : Boolean; overload;
// 업로드 file
// file -> file
class function UploadFile(host : string; port : integer; id, pw : string; sFilename, sTargetname : string; var msg : string;
bOverWrite : Boolean = True) : Boolean; overload;
// memorystream -> file
class function UploadFile(host : string; port : integer; id, pw : string; src : TMemoryStream; sTargetname : string; var msg : string;
bOverWrite : Boolean = True) : Boolean; overload;
// file -> file
class function UploadFile(host, port, id, pw : string; sFilename, sTargetname : string; var msg : string; bOverWrite : Boolean = True)
: Boolean; overload;
// memorystream -> file
class function UploadFile(host, port, id, pw : string; src : TMemoryStream; sTargetname : string; var msg : string; bOverWrite : Boolean = True)
: Boolean; overload;
end;
const
BUFFER_SIZE = 262146;
implementation
{ TSftpClient }
constructor TSftpClient.Create(host : string; port : integer; id, pw : string);
begin
sFtpClient := TScSFTPClient.Create(nil);
sIdHandler := TScIdIOHandler.Create(nil);
sShClient := TScSSHClient.Create(nil);
sMemoryStorage := TScMemoryStorage.Create(nil);
slMsg := TStringList.Create;
try
if FTimeout = 0 then
FTimeout := 3600;
if FVersion = '' then
FVersion := 'vSFTP6';
sRootDir := '/'; // 최상위 경로
// SFTP Client
sFtpClient.sShClient := sShClient;
sFtpClient.ReadBlockSize := BUFFER_SIZE;
sFtpClient.WriteBlockSize := BUFFER_SIZE;
sFtpClient.UseUnicode := True;
sFtpClient.Timeout := Timeout;
// sFtpClient.NonBlocking := True;
// sFtpClient.EventsCallMode := ecSynchronous;
sFtpClient.Version := TScSFTPVersion(GetEnumValue(TypeInfo(TScSFTPVersion), Version));
sFtpClient.OnConnect := SftpClientConnect;
sFtpClient.OnDisconnect := SftpClientDisconnect;
sFtpClient.OnSuccess := SftpClientSuccess;
sFtpClient.OnError := SftpClientError;
sFtpClient.OnDirectoryList := SftpClientDirectoryList;
sFtpClient.OnCreateLocalFile := SftpClientCreateLocalFile;
// Id Handler
sIdHandler.RecvBufferSize := BUFFER_SIZE;
sIdHandler.SendBufferSize := BUFFER_SIZE;
sIdHandler.Client := sShClient;
sIdHandler.ReadTimeout := Timeout;
// TScMemoryStorage
sMemoryStorage.StoreUserPassword := True;
sMemoryStorage.OnCheckUserKey := scmrystrg1CheckUserKey;
sMemoryStorage.OnCheckUserPass := scmrystrg1CheckUserPass;
// SSH Client
sShClient.KeyStorage := sMemoryStorage;
sShClient.Authentication := atPassword;
sShClient.Hostname := host;
sShClient.port := port;
sShClient.User := id;
sShClient.Password := pw;
sShClient.Timeout := Timeout;
sShClient.Options.SocketReceiveBufferSize := BUFFER_SIZE;
sShClient.Options.SocketSendBufferSize := BUFFER_SIZE;
sShClient.Options.ServerAliveCountMax := 5000;
sShClient.HostKeyAlgorithms.AsString := 'ssh-rsa,ssh-dss';
sShClient.OnServerKeyValidate := sshClientServerKeyValidate;
Initialize;
except
on E : Exception do
slMsg.Add(E.Message);
end;
end;
constructor TSftpClient.Create(host, port, id, pw : string);
begin
Create(host, StrToInt(port), id, pw);
end;
destructor TSftpClient.Destroy;
begin
Finalize;
if Assigned(slMsg) then
begin
slMsg.Clear;
{$IFDEF VER220} slMsg.Free; {$ELSE} slMsg.DisposeOf; {$ENDIF}
end;
if Assigned(sMemoryStorage) then
{$IFDEF VER220} sMemoryStorage.Free; {$ELSE} sMemoryStorage.DisposeOf; {$ENDIF}
if Assigned(sShClient) then
{$IFDEF VER220} sShClient.Free; {$ELSE} sShClient.DisposeOf; {$ENDIF}
if Assigned(sIdHandler) then
{$IFDEF VER220} sIdHandler.Free; {$ELSE} sIdHandler.DisposeOf; {$ENDIF}
if Assigned(sFtpClient) then
{$IFDEF VER220} sFtpClient.Free; {$ELSE} sFtpClient.DisposeOf; {$ENDIF}
inherited;
end;
procedure TSftpClient.Initialize; // SFTP 연결
begin
try
sShClient.connect;
if sShClient.Connected then
sFtpClient.Initialize
else
slMsg.Add('not connected sShClient');
// OpenDir(sRootDir);
except
on E : Exception do
slMsg.Add(E.Message);
end;
end;
procedure TSftpClient.Finalize; // SFTP 연결 해제
begin
try
if sShClient.Connected then
sShClient.Disconnect;
if sFtpClient.Active then
sFtpClient.Disconnect;
except
on E : Exception do
slMsg.Add(E.Message);
end;
end;
function TSftpClient.GetConnected : Boolean;
begin
Result := False;
if sShClient.Connected and sFtpClient.Active then
Result := True;
end;
function TSftpClient.GetDirName(const Path : string) : string;
var
sTmpPath : string;
i : integer;
begin
Result := '';
// 폴더 경로 추출
sTmpPath := StringReplace(Path, '/', '\', [rfReplaceAll]);
sTmpPath := ExtractFileDir(sTmpPath);
for i := Length(sTmpPath) downto 1 do
begin
if sTmpPath[i] = '\' then
begin
sRootDir := Copy(sTmpPath, 0, i);
sTmpPath := Copy(sTmpPath, Length(sTmpPath) - i, i + 1);
Break;
end;
end;
// SFTP에 디렉토리가 없으면 디렉토리 생성을 위해 경로 \ -> ''로 바꾸기 1
Result := StringReplace(sTmpPath, '\', '/', [rfReplaceAll]);
end;
function TSftpClient.GetRootDir : string;
begin
Result := sRootDir;
if IsDelimiter('/', Result, Length(Result)) and (Result[Length(Result)] = '/') then
Exit;
if IsDelimiter('\', Result, Length(Result)) and (Result[Length(Result)] = '\') then
Exit;
Result := Result + '/';
end;
function TSftpClient.IsDirectoryExists(const sSRootDir, sDirectory : string) : Boolean;
var
i : integer;
List : TCRObjectList;
Handle : TScSFTPFileHandle;
begin
Result := False;
Handle := sFtpClient.OpenDirectory(sSRootDir);
List := TCRObjectList.Create;
try
sFtpClient.ReadDirectoryToList(Handle, List);
for i := 0 to List.Count - 1 do
begin
if (TScSFTPFileInfo(List.Items[i]).Longname[1] = 'd') and (TScSFTPFileInfo(List.Items[i]).FileName = sDirectory) then
begin
Result := True;
Break;
end;
end;
finally
sFtpClient.CloseHandle(Handle);
FreeAndNil(List);
end;
end;
procedure TSftpClient.OpenDir(const Path : string; const SelectedName : string = '');
var
Handle : TScSFTPFileHandle;
RootDir : string;
begin
RootDir := sFtpClient.RetrieveAbsolutePath(Trim(Path));
Handle := sFtpClient.OpenDirectory(RootDir);
sRootDir := RootDir;
try
try
while not sFtpClient.Eof(Handle) do
sFtpClient.ReadDirectory(Handle);
except
on E : Exception do
begin
OutputDebugString(PWideChar(E.Message));
slMsg.Add(Format('OpenDir Error : %s', [E.Message]));
end;
end;
finally
sFtpClient.CloseHandle(Handle);
end;
end;
procedure TSftpClient.RemoveDir(const Path : string);
begin
//
end;
procedure TSftpClient.RenameFile(sOldName, sNewname : string; bOverWrite : Boolean = True);
begin
slMsg.Clear;
sOldName := StringReplace(sOldName, '\', '/', [rfReplaceAll]);
sNewname := StringReplace(sNewname, '\', '/', [rfReplaceAll]);
try
if bOverWrite then
begin
sFtpClient.RenameFile(sOldName, sNewname, [TScSFTPRenameFlag.rfOverwrite]);
end
else
begin
{
Filename : Unpaid_20201118_01.dat, Error Cdoe : 8, Error Message : Operation unsupported
https://www.devart.com/sbridge/docs/index.html?tscsftprenameflag.htm
rfNative if this flag is included and there is not possible to replace the destination in an atomic fashion, then the SSH_FX_OP_UNSUPPORTED error will be returned
}
sFtpClient.RenameFile(sOldName, sNewname, []);
end;
except
on E : Exception do
slMsg.Add(Format('RenameFile Error : %s', [E.Message]));
end;
end;
procedure TSftpClient.scmrystrg1CheckUserKey(Sender : TObject; ClientInfo : TScSSHClientInfo; Key : TScKey; var Accept : Boolean);
begin
Accept := True;
end;
procedure TSftpClient.scmrystrg1CheckUserPass(Sender : TObject; ClientInfo : TScSSHClientInfo; const Password : string; var Accept : Boolean);
begin
Accept := True;
slMsg.Add(Format('Password : %s', [Password]));
end;
procedure TSftpClient.MakeDir(const DirName : string);
begin
if DirName <> '' then
sFtpClient.MakeDirectory(GetRootDir + DirName);
OpenDir(GetRootDir + DirName);
end;
function TSftpClient.DownloadFile(sFilename : string; Target : TMemoryStream) : Boolean;
var
tmpStream : TMemoryStream;
begin
Result := False;
slMsg.Clear;
sFilename := StringReplace(sFilename, '\', '/', [rfReplaceAll]);
tmpStream := TMemoryStream.Create;
try
try
sFtpClient.DownloadToStream(GetRootDir + sFilename, tmpStream, 0);
Target.Clear;
Target.LoadFromStream(tmpStream);
Result := True;
except
on E : Exception do
begin
slMsg.Add(Format('Download Error : %s', [E.Message]));
Result := False;
end;
end;
finally
tmpStream.Free;
end;
end;
function TSftpClient.DownloadFile(sFilename, sTargetname : string; bOverWrite : Boolean = True) : Boolean;
var
bNext : Boolean;
begin
Result := False;
bNext := True;
slMsg.Clear;
sFilename := StringReplace(sFilename, '\', '/', [rfReplaceAll]);
sTargetname := StringReplace(sTargetname, '/', '\', [rfReplaceAll]);
if not SysUtils.DirectoryExists(ExtractFilePath(sTargetname)) then
bNext := SysUtils.ForceDirectories(ExtractFilePath(sTargetname)); // 폴더 생성.
if bNext then
begin
try
sFtpClient.DownloadFile(GetRootDir + sFilename, sTargetname, bOverWrite, 0);
except
on E : Exception do
begin
slMsg.Add(Format('Download Error : %s', [E.Message]));
Result := False;
end;
end;
end;
if FileExists(sTargetname) then
Result := True;
end;
function TSftpClient.UploadFile(src : TMemoryStream; sTargetname : string; bOverWrite : Boolean = True) : Boolean;
var
tmpStream : TMemoryStream;
begin
Result := False;
slMsg.Clear;
// 파일 경로 바꾸기
sTargetname := StringReplace(sTargetname, '\', '/', [rfReplaceAll]);
tmpStream := TMemoryStream.Create;
try
try
src.SaveToStream(tmpStream);
// src.Seek(0, soFromBeginning);
// tmpStream.CopyFrom(src, src.Size);
// tmpStream.Seek(0, soFromBeginning);
src.Clear;
sFtpClient.UploadFromStream(tmpStream, GetRootDir + sTargetname, bOverWrite, 0);
Result := True;
except
on E : Exception do
begin
slMsg.Add(Format('Upload Error : %s', [E.Message]));
Result := False;
end;
end;
finally
tmpStream.Free;
end;
end;
function TSftpClient.UploadFile(sFilename, sTargetname : string; bOverWrite : Boolean = True) : Boolean;
begin
Result := False;
slMsg.Clear;
// 업로드 할 로컬 파일이 존재 해야 진행
if FileExists(sFilename) then
begin
sTargetname := StringReplace(sTargetname, '\', '/', [rfReplaceAll]);
try
sFtpClient.UploadFile(sFilename, GetRootDir + sTargetname, bOverWrite, 0);
Result := True;
except
on E : Exception do
begin
slMsg.Add(Format('Upload Error : %s', [E.Message]));
Result := False;
end;
end;
end;
end;
procedure TSftpClient.SetTimeOut(value : integer);
begin
sFtpClient.Timeout := value;
sIdHandler.ReadTimeout := value;
sShClient.Timeout := value;
end;
procedure TSftpClient.SftpClientConnect(Sender : TObject);
begin
OutputDebugString(PWideChar('Sftp Clinet Connect'));
slMsg.Add(Format('%s - Sftp Clinet Connect', [FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz', Now)]));
end;
procedure TSftpClient.SftpClientCreateLocalFile(Sender : TObject; const LocalFileName, RemoteFileName : string; Attrs : TScSFTPFileAttributes;
var Handle : {$IFDEF VER220} Cardinal {$ELSE} NativeUInt {$ENDIF});
var
dwFlags : DWORD;
begin
if aAttrs in Attrs.ValidAttributes then
begin
dwFlags := 0;
if faReadOnly in Attrs.Attrs then
dwFlags := dwFlags or FILE_ATTRIBUTE_READONLY;
if faSystem in Attrs.Attrs then
dwFlags := dwFlags or FILE_ATTRIBUTE_SYSTEM;
if faHidden in Attrs.Attrs then
dwFlags := dwFlags or FILE_ATTRIBUTE_HIDDEN;
if faArchive in Attrs.Attrs then
dwFlags := dwFlags or FILE_ATTRIBUTE_ARCHIVE;
if faCompressed in Attrs.Attrs then
dwFlags := dwFlags or FILE_ATTRIBUTE_COMPRESSED;
end
else
dwFlags := FILE_ATTRIBUTE_NORMAL;
Handle := CreateFile(PChar(LocalFileName), GENERIC_READ or GENERIC_WRITE, 0, nil, CREATE_NEW, dwFlags, 0);
end;
procedure TSftpClient.SftpClientDirectoryList(Sender : TObject; const Path : string; const Handle : TBytes; FileInfo : TScSFTPFileInfo;
Eof : Boolean);
begin
if (FileInfo = nil) or (FileInfo.FileName = '.') then
Exit;
end;
procedure TSftpClient.SftpClientDisconnect(Sender : TObject);
begin
//
OutputDebugString(PWideChar('Sftp Clinet DisConnect'));
slMsg.Add(Format('%s - Sftp Clinet DisConnect', [FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz', Now)]));
end;
procedure TSftpClient.SftpClientError(Sender : TObject; Operation : TScSFTPOperation; const FileName : string; const Handle : TArray<Byte>;
ErrorCode : integer; const ErrorMessage : string; var Fail : Boolean);
begin
//
slMsg.Add(Format('Filename : %s, Error Cdoe : %d, Error Message : %s', [FileName, ErrorCode, ErrorMessage]));
end;
procedure TSftpClient.SftpClientSuccess(Sender : TObject; Operation : TScSFTPOperation; const FileName : string; const Handle : TArray<Byte>;
const Message : string);
begin
//
slMsg.Add(Format('Filename : %s, Success Message : %s', [FileName, Message]));
end;
procedure TSftpClient.sshClientServerKeyValidate(Sender : TObject; NewServerKey : TScKey; var Accept : Boolean);
var
CurHostKeyName : string;
Key : TScKey;
fp : string;
begin
if sShClient.HostKeyName = '' then
CurHostKeyName := sShClient.Hostname
else
CurHostKeyName := sShClient.HostKeyName;
Key := sMemoryStorage.Keys.FindKey(CurHostKeyName);
if (Key = nil) or not Key.Ready then
begin
NewServerKey.GetFingerPrint(haMD5, fp);
// slMsg.add(Format('fp : %s, CurHostKeyName : %s', [fp, CurHostKeyName]));
Key := TScKey.Create(nil);
try
Key.Assign(NewServerKey);
Key.KeyName := CurHostKeyName;
sMemoryStorage.Keys.Add(Key);
except
on E : Exception do
begin
slMsg.Add(E.Message);
Key.Free;
end;
end;
Accept := True; // 로그인시 필요.
end;
end;
{ TSftpClientRun }
class function TSftpClientRun.DownloadFile(host : string; port : integer; id, pw, sFilename, sTargetname : string; var msg : string;
bOverWrite : Boolean = True) : Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.DownloadFile(sFilename, sTargetname, bOverWrite);
except
on E : Exception do
v.slMsg.Add(E.Message);
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
class function TSftpClientRun.DownloadFile(host : string; port : integer; id, pw, sFilename : string; Target : TMemoryStream; var msg : string)
: Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.DownloadFile(sFilename, Target);
except
on E : Exception do
v.slMsg.Add(E.Message);
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
class function TSftpClientRun.DownloadFile(host, port, id, pw, sFilename, sTargetname : string; var msg : string; bOverWrite : Boolean = True)
: Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.DownloadFile(sFilename, sTargetname, bOverWrite);
except
on E : Exception do
v.slMsg.Add(E.Message);
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
class function TSftpClientRun.DownloadFile(host, port, id, pw, sFilename : string; Target : TMemoryStream; var msg : string) : Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.DownloadFile(sFilename, Target);
except
on E : Exception do
v.slMsg.Add(E.Message);
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
class function TSftpClientRun.UploadFile(host : string; port : integer; id, pw, sFilename, sTargetname : string; var msg : string;
bOverWrite : Boolean = True) : Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.UploadFile(sFilename, sTargetname, bOverWrite);
except
on E : Exception do
begin
v.slMsg.Add(E.Message);
end;
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
class function TSftpClientRun.UploadFile(host : string; port : integer; id, pw : string; src : TMemoryStream; sTargetname : string; var msg : string;
bOverWrite : Boolean = True) : Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.UploadFile(src, sTargetname, bOverWrite);
except
on E : Exception do
begin
v.slMsg.Add(E.Message);
end;
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
class function TSftpClientRun.UploadFile(host, port, id, pw, sFilename, sTargetname : string; var msg : string; bOverWrite : Boolean = True)
: Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.UploadFile(sFilename, sTargetname, bOverWrite);
except
on E : Exception do
begin
v.slMsg.Add(E.Message);
end;
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
class function TSftpClientRun.UploadFile(host, port, id, pw : string; src : TMemoryStream; sTargetname : string; var msg : string;
bOverWrite : Boolean = True) : Boolean;
var
v : TSftpClient;
begin
Result := False;
v := TSftpClient.Create(host, port, id, pw);
try
try
Result := v.UploadFile(src, sTargetname, bOverWrite);
except
on E : Exception do
begin
v.slMsg.Add(E.Message);
end;
end;
finally
msg := v.slMsg.text;
v.Free;
end;
end;
end.
Delphi SSH, SFTP, FTPS, SSL, HTTP/HTTPS, WebSocket and SignalR Сlient and Server (devart.com)
SecureBridge - Library of Nonvisual Components
Cross-Platform Solution for Delphi and C++Builder The high-performance and feature-rich component library offers cross-platform solutions for developing applications using various IDEs and editions including Community Edition: RAD Studio, Delphi, C++Builde
www.devart.com
컴포넌트 사용하기.
'DELPHI(델파이)' 카테고리의 다른 글
[델파이 - DELPHI] m4a to wav(PCM) (0) | 2020.11.24 |
---|---|
[델파이 - DELPHI] SFTP Server Service (Devart SecureBridge) (0) | 2020.11.24 |
[델파이 - DELPHI] HTTPS 서버 사용시 다른 포트 바인딩 주의점 (0) | 2020.11.24 |
[델파이 - DELPHI] INDY HTTP, TCP TLS 1.2 사용 (0) | 2020.11.24 |
[델파이 - DELPHI] WINDOWS10 알림창 (0) | 2020.11.19 |