using KssSmaPlaLib.Commons; using KssSmaPlaLib.Encryption; using KssSmaPlaLib.IO.File; using Renci.SshNet; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace KssSmaPlaLib.Communications { public class SftpUxer { public const string INIDIC_KEY_HOST = "Host"; public const string INIDIC_KEY_PORT = "Port"; public const string INIDIC_KEY_USERNAME = "UserName"; public const string INIDIC_KEY_PRIVATE_KEY_FILE_PATH = "PrivateKeyFilePath"; public const string INIDIC_KEY_PASS_PHRASE = "PassPhrase"; private readonly string _host; private readonly int _port; private readonly string _username; private readonly string _privateKeyPath; // PuTTY形式でもOK private readonly string _passPhrase; public static bool TryCreateByDic(Dictionary iniDic, out SftpUxer sftp, out string resultMessage) { sftp = null; resultMessage = string.Empty; // ホスト名 var host = DictionaryUxer.GetValueOrDefault(iniDic, INIDIC_KEY_HOST, string.Empty); if (string.IsNullOrEmpty(host)) { resultMessage = $"SFTPのホスト名が指定されていません。[キー:{INIDIC_KEY_HOST}]"; return false; } // ポート番号 var strPort = DictionaryUxer.GetValueOrDefault(iniDic, INIDIC_KEY_PORT, string.Empty); if (int.TryParse(strPort, out var portNo) == false) { LoggerUxer.Error($"SFTPのポート番号が未指定または数値でありません。[キー:{INIDIC_KEY_PORT},ポート番号:{strPort}]"); return false; } // ユーザ名 var username = DictionaryUxer.GetValueOrDefault(iniDic, INIDIC_KEY_USERNAME, string.Empty); if (string.IsNullOrEmpty(username)) { LoggerUxer.Error($"SFTPのユーザ名が指定されていません。[キー:{INIDIC_KEY_USERNAME}]"); return false; } // プライベートキーファイルパス var privateKeyPath = DictionaryUxer.GetValueOrDefault(iniDic, INIDIC_KEY_PRIVATE_KEY_FILE_PATH, string.Empty); if (string.IsNullOrEmpty(privateKeyPath)) { LoggerUxer.Error($"SFTPのプライベートキーファイルパスが指定されていません。[キー:{INIDIC_KEY_PRIVATE_KEY_FILE_PATH}]"); return false; } if (!File.Exists(privateKeyPath)) { LoggerUxer.Error($"SFTPのプライベートキーファイルパスが存在しません。[キー:{INIDIC_KEY_PRIVATE_KEY_FILE_PATH},パス:{privateKeyPath}]"); return false; } // パスフレーズ var passPhrase = DictionaryUxer.GetValueOrDefault(iniDic, INIDIC_KEY_PASS_PHRASE, string.Empty); // ↓パスフレーズなしの場合もあり(必須としない) //if (string.IsNullOrEmpty(passPhrase)) //{ // LoggerUxer.Error($"SFTPのパスフレーズが指定されていません。[キー:{INIDIC_KEY_PASS_PHRASE}]"); // return false; //} var passPhraseDec = string.Empty; if (!string.IsNullOrEmpty(passPhrase)) { // パスフレーズの復号化 if (!SimpleEncryptUxer.Decrypt(passPhrase, out passPhraseDec, out string messageForMe)) { LoggerUxer.Error($"SFTPのパスフレーズ復号化で失敗しました。[キー:{INIDIC_KEY_PASS_PHRASE},エラー内容:{messageForMe}]"); return false; } } // オブジェクト生成 sftp = new SftpUxer(host, portNo, username, privateKeyPath, passPhraseDec); return true; } private SftpUxer(string host, int port, string username, string privateKeyPath, string passPhrase) { _host = host; _port = port; _username = username; _privateKeyPath = privateKeyPath; _passPhrase = passPhrase; } public bool TestConnect(out string resultMessage) { resultMessage = string.Empty; try { PrivateKeyFile keyFile; if(!string.IsNullOrEmpty(_passPhrase)) { // パスフレーズあり keyFile = new PrivateKeyFile(_privateKeyPath, _passPhrase); } else { // パスフレーズなし keyFile = new PrivateKeyFile(_privateKeyPath); } var keyFiles = new[] { keyFile }; using (var client = new SftpClient(_host, _port, _username, keyFiles)) { client.Connect(); client.Disconnect(); } return true; } catch (Exception ex) { resultMessage = $"SFTPのテスト接続に失敗しました。[ホスト:{_host},ポート:{_port},ユーザ名:{_username},プライベートキーファイルパス:{_privateKeyPath},エラー内容:{ex.Message}]"; return false; } } public bool Upload(List filePathList, string remotePath, out string resultMessage) { resultMessage = string.Empty; if (string.IsNullOrEmpty(remotePath)) remotePath = "."; try { if (!remotePath.EndsWith("/")) remotePath += "/"; PrivateKeyFile keyFile; if (!string.IsNullOrEmpty(_passPhrase)) { keyFile = new PrivateKeyFile(_privateKeyPath, _passPhrase); } else { keyFile = new PrivateKeyFile(_privateKeyPath); } var keyFiles = new[] { keyFile }; using (var client = new SftpClient(_host, _port, _username, keyFiles)) { client.Connect(); foreach (var filePath in filePathList) { using (var fileStream = new FileStream(filePath, FileMode.Open)) { client.UploadFile(fileStream, remotePath + Path.GetFileName(filePath)); } } client.Disconnect(); } return true; } catch (Exception ex) { resultMessage = $"SFTPのアップロードでエラーが発生しました。[ファイル:{string.Join(",", filePathList)},リモートパス:{remotePath},エラー内容:{ex.Message}]"; return false; } } public bool Download(string remotePath, string localPath, out string resultMessage) { resultMessage = string.Empty; try { var keyFile = new PrivateKeyFile(_privateKeyPath); var keyFiles = new[] { keyFile }; using (var client = new SftpClient(_host, _port, _username, keyFiles)) { client.Connect(); using (var fileStream = new FileStream(localPath, FileMode.Create)) { client.DownloadFile(remotePath, fileStream); } client.Disconnect(); } return true; } catch (Exception ex) { resultMessage = $"SFTPのダウンロード(Download)でエラーが発生しました。[エラー内容:{ex.Message}]"; return false; } } public bool DownloadMatchingFiles( string remoteDir, string localDir, out int downloadedFileCount, out string resultMessage, string pattern = "*.txt") { downloadedFileCount = 0; resultMessage = string.Empty; if (string.IsNullOrEmpty(remoteDir)) remoteDir = "."; if (string.IsNullOrEmpty(localDir)) localDir = "."; if (!Directory.Exists(localDir)) { resultMessage = $"ダウンロード先のローカルフォルダが存在しません。[ローカルフォルダ:{localDir}]"; return false; } try { PrivateKeyFile keyFile; if (!string.IsNullOrEmpty(_passPhrase)) { keyFile = new PrivateKeyFile(_privateKeyPath, _passPhrase); } else { keyFile = new PrivateKeyFile(_privateKeyPath); } using (var client = new SftpClient(_host, _port, _username, keyFile)) { client.Connect(); // リモートディレクトリ内のファイル一覧を取得 var files = client.ListDirectory(remoteDir) .Where(f => !f.IsDirectory && StringUxer.WildcardMatch(f.Name, pattern)); foreach (var file in files) { string localPath = Path.Combine(localDir, file.Name); using (var fs = new FileStream(localPath, FileMode.Create)) { client.DownloadFile(file.FullName, fs); Console.WriteLine($"1ファイルダウンロードしました。[ファイル名:{file.Name}]"); downloadedFileCount++; } } client.Disconnect(); } return true; } catch (Exception ex) { resultMessage = $"SFTPのダウンロード(DownloadMatchingFiles)でエラーが発生しました。[エラー内容:{ex.Message}]"; return false; } } public bool DeleteMatchingFiles( string remoteDir, out int deletedFileCount, out string resultMessage, string pattern = "*.txt") { deletedFileCount = 0; resultMessage = string.Empty; try { var keyFile = new PrivateKeyFile(_privateKeyPath, _passPhrase); using (var client = new SftpClient(_host, _port, _username, keyFile)) { client.Connect(); // リモートディレクトリ内のファイル一覧を取得 var files = client.ListDirectory(remoteDir) .Where(f => !f.IsDirectory && StringUxer.WildcardMatch(f.Name, pattern)); foreach (var file in files) { string remotePath = PathUxer.CombineUnixPath(remoteDir, file.Name); client.DeleteFile(remotePath); deletedFileCount++; } client.Disconnect(); } return true; } catch (Exception ex) { resultMessage = $"SFTPのファイル削除でエラーが発生しました。[エラー内容:{ex.Message}]"; return false; } } } }