中堅プログラマーの備忘録

忘れっぽくなってきたので備忘録として・・・

【C#.net】FtpWebRequestがあまりにも使えないのでFluentFTPを使う

1.概要

FTPSクライアント機能を持ったアプリケーションを作成していた時に
下記の不具合に遭遇しました。

不具合が確認できた条件としては
サーバはHTTPサーバとしての機能はあるが、FTPサーバーとしての機能は未実装
でありました。

そんな中、HTTP部分のデバッグを行っていたのですが
アプリケーションがハングアップしているような状況に陥りました。

原因を探っていると
FtpWebRequest.GetRequestStream()の部分で
約20秒程停止し、

WebException:リモートサーバーに接続できません。

とのエラーが発生していました。

この時は、FTPサーバーをまだ作ってないから仕方ない。
とりあえずサーバーがない時のタイムアウトを短くしよう。

と考えプロパティーを探してみましたが
なんと、接続先を探している間のタイムアウト時間は設定が出来ないことが判明しました。


【FtpWebRequest.Timeout】プロパティというのが存在するのですが
これは要求に対する待機時間で、接続完了してからの部分になります。

さすがにこれでは使い物にならないですし、非同期にしたところで
20秒の応答待ち時間が発生してしまうリスクを抱えるのは
いただけないとのことで何かいい方法はないかと探していたら


Microsoft公式ページに

WebExceptionがスローされてから要求のタイムアウトが示されるまでに15秒以上かかることがあります。

との記述が・・・



更に

新しい開発にはFtpWebRequestクラスを使用しないことをお勧めします。

との記述も・・・



正直心が折れますが、どう対応するか検討することにしました。

2.対策

どのように対策したらいいか探していたら
下記の記述を見つけました。


For FtpWebRequest: use third party FTP client (e.g. from this list).


要するに【FtpWebRequest】は使わずにサードパーティのFTPクライアントを使って下さい、といった内容になります。


詳しくは下記のURLに記載があります。
https://stackoverflow.com/questions/1371964/free-ftp-library


このページの中に
以前は【System.Net.FtpClient】という名前だった【FluentFTP】が使えますよ
と紹介されていましたので、これを試してみようかと思います。

3.FluentFTPを使う

パッケージの追加は【NuGet】から行います。

【ツール】→【NuGetパッケージマネージャー】→【ソリューションのNuGetパッケージの管理】
をクリックします。
f:id:tsu--kun:20191025142857p:plain


検索ボックスに【FluentFTP】と入力しインストールします。
f:id:tsu--kun:20191025142909p:plain


これで準備は完了になります。

4.スクリプト

とりあえずファイルをアップロードするスクリプトで試してみました。
接続タイムアウト時間を5秒に設定し【FTPS】を想定して記述しています。
証明書の内容の検証は行ず、全て【true】を返すようになっています。

using FluentFTP;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FluentFTPtest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            FtpClient client = new FtpClient();

            client.Host = "192.168.0.129";
            client.Port = 61234;
            // 資格情報の設定
            client.Credentials = new NetworkCredential("testuser", "testuser");
            // 要求の完了後に接続を閉じる
            client.SocketKeepAlive = false;
            // Explicit設定
            client.EncryptionMode = FtpEncryptionMode.Explicit;
            // プロトコルはTls
            client.SslProtocols = SslProtocols.Tls;
            // 接続タイムアウトを5秒に設定
            client.ConnectTimeout = 5000;
            // 証明書の内容を確認しない
            client.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);
            
            try
            {
                // 接続
                client.Connect();
                // ファイルのアップロード
                client.UploadFile(@"C:\work\1.png", "1.png");

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                // 切断
                client.Disconnect();
                // 解放
                client.Dispose();
            }
        }

        private void OnValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
        {
            // 証明書の内容を確認しない
            e.Accept = true;
        }
    }
}

詳しい解説はこちら
https://github.com/robinrodricks/FluentFTP#ftps

5.使ってみた感想

【FtpWebRequest】が使えないと判明した時はどうしようか迷いましたが
【FluentFTP】の方が機能が充実しており、使いやすいと感じました。

また、今回やりたかったタイムアウトも5秒後に

Timed out trying to connect!

と表示され、しっかりと結果が確認出来ました。

Microsoftも【FtpWebRequest】の使用は推奨していない中で
今後は【FluentFTP】を使っていくことになりそうです。