LoginSignup
17
19

More than 3 years have passed since last update.

C#でSSH.NETを使ってsftpサーバとやりとりをする

Last updated at Posted at 2019-04-20

要約

C# SSH.NET の使用例です。
sshでsftpサーバに接続してファイルリストを取得します。

GitHubにサンプルコードを置いてあります。(https://github.com/unknown-ds/csharp_sftp)

環境

Windows 10
Visual Studio Express 2017 for Windows Desktop
Docker Desktop Community ver.2.0.0.3(31259)

参考

テスト用に以下のdocker-composeファイルを使わせて頂きました。ありがとうございます。:grinning:
テスト用途のSFTP/FTPS/FTPサーバーを単独で起動するdocker-compose.ymlの例

実装

フォルダ構成

プロジェクトフォルダ以下にsftpサーバ用dockerフォルダを作っています。この下にアクセステスト用フォルダremoteと鍵が入っているフォルダsshがあります。

└─[projectroot]
  ├─[docker]
  │ ├─[remote]
  │ │ └─test.txt   // テスト用テキストファイル
  │ └─[ssh]
  │   └─id_rsa      // 秘密鍵
  └─docker-compose.yml

ライブラリ

NugetでSSH.NETをインストールします。

lib.png

テストクラス説明

コンストラクタで接続情報を用意し、Execute()でテストをしています。
基本的な流れとしては
1. Renci.SshNet.ConnectionInfoクラスを生成し、接続情報を用意する。
2. Renci.SshNet.SftpClientのインスタンスを生成して接続/切断を行う。
3. ファイルリストを取得する場合はListDirectoryで取得して処理。
4. ファイルのアップロード/ダウンロードはUploadFile/DownloadFileメソッドにストリームを渡して処理する。
となります。

// SFTP接続クラス
    public class CSftp
    {
        // 接続情報
        public ConnectionInfo ConnNfo { private set; get; }
        // 接続ホスト名
        public string HostName { private set; get; }
        // ポート
        public Int32 Port { private set; get; }
        // ユーザー名
        public string UserName { private set; get; }
        // パスワード
        public string Password { private set; get; }

        // コンストラクタ
        public CSftp()
        {
            HostName = "localhost";             // 接続先ホスト名
            Port = 10221;                       // ポート
            UserName = "sftp-with-rsa-key";     // ユーザー名
            Password = "1001";                  // パスワード

            string KeyFile = @"..\..\docker\ssh\id_rsa";     // 秘密鍵
            string PassPhrase = "";                          // パスフレーズ


            // パスワード認証
            var _PassAuth = new PasswordAuthenticationMethod(UserName, Password);

            // 秘密鍵認証
            var _PrivateKey = new PrivateKeyAuthenticationMethod(UserName, new PrivateKeyFile[]{
                        new PrivateKeyFile(KeyFile, PassPhrase)
                    });

            // 接続情報の生成
            ConnNfo = new ConnectionInfo(HostName, Port, UserName,
                new AuthenticationMethod[]{
                    _PassAuth,          // パスワード認証
                    _PrivateKey,        // 秘密鍵認証
                }
            );

        }

        // 実行
        public void Execute()
        {

            using (var sftp = new SftpClient(ConnNfo))
            {
                // 接続
                sftp.Connect();
                // 確認
                if (sftp.IsConnected)
                {
                    // 接続に成功
                    Console.WriteLine("Connection success!!\n");
                }
                else
                {
                    // 接続に失敗
                    Console.WriteLine("Connection failed!!\n");
                    return;
                }


                // ファイルリスト表示
                printFiles(sftp, "/remote");

                // ファイル内容表示
                printTxtFile(sftp, "/remote/test.txt");

                // ファイルアップロード
                uploadFile(sftp, "/remote", "../../Program.cs");


                // 切断
                sftp.Disconnect();
            }

        }

        // ファイル表示
        private void printFiles(
            SftpClient _sftp,   // sftpクライアント
            string _Path        // パス
            )
        {
            // 指定パスを調べる
            foreach (var file in _sftp.ListDirectory(_Path))
            {
                if (file.Name.StartsWith(".")) continue;

                if (file.IsDirectory)
                {
                    // ディレクトリなら再帰して調べる
                    printFiles(_sftp, file.FullName);
                }
                else
                {
                    // 表示
                    Console.WriteLine($"{file.FullName}\t\t{file.LastAccessTime}\t{file.LastWriteTime}");
                }
            }
        }

        // 指定テキストファイルの表示
        private void printTxtFile(
            SftpClient _sftp,       // sftpクライアント
            string _FilePath        // ファイルパス
            )
        {
            var _CurDir = Path.GetDirectoryName(_FilePath).Substring(1);
            var _FileName = Path.GetFileName(_FilePath);

            // カレントディレクトリ変更
            _sftp.ChangeDirectory(_CurDir);

            foreach (var file in _sftp.ListDirectory("./"))
            {
                if (file.IsDirectory) continue;
                if (file.Name != _FileName) continue;

                // 読み込み
                Int64 _Size = file.Length;
                var _Buf = new byte[_Size];
                using (var _St = new MemoryStream(_Buf, 0, (int)_Size))
                {
                    _sftp.DownloadFile(file.FullName, _St);
                }

                // SJIS変換
                string _str = Encoding.GetEncoding(932).GetString(_Buf);
                // 内容表示
                Console.WriteLine();
                Console.WriteLine($"------------------{file.Name}");
                Console.WriteLine($"{_str}");
                Console.WriteLine("------------------");
            }
        }

        // ファイルのアップロード
        private void uploadFile(
            SftpClient _sftp,       // sftpクライアント
            string  _UploadPath,    // アップロードパス
            string  _UploadFile     // アップロードファイル名
            )
        {
            // カレントディレクトリ変更
            _sftp.ChangeDirectory(_UploadPath);
            // アップロード先パス
            var _RemotePath = _UploadPath + "/" + Path.GetFileName(_UploadFile);

            using (var _uploadStream = File.OpenRead(_UploadFile))
            {
                _sftp.UploadFile(_uploadStream, _RemotePath, true);
            }
        }        
    }

注意点

日本語ファイル名を扱う際は、ConnectionInfoクラスのEncodingプロパティにエンコード情報を設定する。

実行例

docker-compose up -dでsftpサーバを立ち上げます。
テストコードを実行し接続に成功すると以下のような出力になります。
ファイルリストの表示とtest.txtの内容表示、Program.csのアップロードが行われます。

Connection success!!

/remote/test.txt                2019/04/16 11:17:52     2019/04/16 15:29:18
/remote/Program.cs              2019/04/16 16:40:02     2019/04/16 16:40:02

------------------test.txt
テスト用のテキストです。
------------------

SSH接続してコマンドで行う

シェルコマンドでやりとりする場合は、SshClientクラスインスタンスを生成してやり取りをすることができます。

using (var sshclient = new SshClient(ConnNfo))
{
    sshclient.Connect();

    using (var cmd = sshclient.CreateCommand("ls -lah"))
    {
        Console.WriteLine(cmd.Execute());
        Console.WriteLine($"ExitStatus:{cmd.ExitStatus}");
    }
        sshclient.Disconnect();
}

シェル実行できない場合は(サーバ側で制限されている場合は)
This service allows sftp connections only.
という風にレスポンスが返ってきます。(ExistStatus は 1 です。)

17
19
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
19