スキップしてメイン コンテンツへ

 none
ftp接続でファイルを連続でアップロードすると2回目以降エラー RRS feed

  • 質問

  • お世話になります。

    下記のようにしてループでファイルをアップロードしようとすると2回目以降エラーになります。
    1回目は、正しくアップロードされています。フォルダーもなければ作成されています。
    2回目以降、失敗しているようですが問題点などわかりましたらご指摘ください。
    また、環境情報が足らなければご指摘ください。長文で申し訳ありません。

        List<string> folders = new List<string>();  28478・01_20190904.pdfのようなファイル名がある
        string ftpFolder;

        freach(var txt in folders){
             string[] dir = txt2[0].Split('_');
             ftpFolder = dir[0]; // + '/' + dir[1];   前半部分をフォルダーとして作成する

             if (ftp.FtpExistDirectory(ftpFolder) == false)   <= 2回目のここでエラー
                  ftp.FtpMakeDirectory(ftpFolder);

             ftp.FtpUploadDupli(ftpFolder + '/' + txt, txt);
        }

    2回目にftp.FtpExistDirectory(ftpFolder)を実行すると「リモート サーバーがエラーを返しました: 226 Transfer complete.」

    ※ using(FtpWebResponse ftpRes =GetResponse()・・・は、ググってる時にusingを使った方がいい!?というのを見かけたのでこうしてます。

    ftp.cs
    public bool FtpExistDirectory(string dir)
    {
         if (!FtpOpen(dir))
              return false;

         ftpReq.Method = System.Net.WebRequestMethods.Ftp.ListDirectory;

         try
         {
              2回目以降、ここでスローされる
              using (System.Net.FtpWebResponse ftpRes = (System.Net.FtpWebResponse)ftpReq.GetResponse()) { }
         }
         catch (WebException e)
         {
             if (e.Status == WebExceptionStatus.ProtocolError)
             {
                 FtpWebResponse r = (FtpWebResponse)e.Response;
                 if (r.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
                 {
                      return false;
                 }
             }

             ftpRes.Close();
             throw; // ファイル関連以外の例外は再スロー
         }
         finally
         {
         }
         return true;
    }
    internal void FtpMakeDirectory(string txt)
    {
         if (!FtpOpen(txt))
              return;

         ftpReq.KeepAlive = false;
         //PASSIVEモードを無効にする
         ftpReq.UsePassive = false;

         //MethodにWebRequestMethods.Ftp.DeleteFile(DELE)を設定
         ftpReq.Method = System.Net.WebRequestMethods.Ftp.MakeDirectory;

         //FtpWebResponseを取得
         System.Net.FtpWebResponse ftpRes = (System.Net.FtpWebResponse)ftpReq.GetResponse();


          ftpRes.Close();
    }
    public void FtpUploadDupli(string mei, string fulPath = "")
    {
          if (!FtpOpen(mei))
              return;

          try
          {
               // MethodにWebRequestMethods.Ftp.UploadFile("STOR")を設定
               ftpReq.Method = System.Net.WebRequestMethods.Ftp.UploadFile;       // UploadFileWithUniqueName;
               // 要求の完了後に接続を閉じる
               ftpReq.KeepAlive = false;
               // ASCIIモードで転送する
               ftpReq.UseBinary = false;
               // PASVモードにする
               ftpReq.UsePassive = true;

               ftpReq.Proxy = null;
               using (ftpRes = (System.Net.FtpWebResponse)ftpReq.GetResponse())
               {
                   //ファイルをアップロードするためのStreamを取得
                   System.IO.Stream reqStrm = ftpReq.GetRequestStream();

                   長くなるので割愛
               }
           }
           catch (Exception ex)
           {
               MessageBox.Show(ex.Message);
               ftpReq.Abort();
           }
     }
    private bool FtpOpen(string mei = "")
    {
         if (adr.Length == 0)
         {
              MessageBox.Show("Uri名が指定されていません", "確認", MessageBoxButtons.OK, MessageBoxIcon.Question);
              return false;
         }
         if (usr.Length == 0 || pwl.Length == 0)
         {
              MessageBox.Show("ユーザー名かパスワードがセットされていません", "確認", MessageBoxButtons.OK, MessageBoxIcon.Question);
              return false;
         }

         char xhk = adr[adr.Length - 1];
         if (xhk != '/')
              adr += "/";    // '/';

         Uri u = new Uri(adr + mei);
         //FtpWebRequestの作成
         ftpReq = (System.Net.FtpWebRequest)System.Net.WebRequest.Create(u);

         //ログインユーザー名とパスワードを設定
         ftpReq.Credentials = new System.Net.NetworkCredential(usr, pwl);

         return true;
    }

            System.Net.FtpWebRequest ftpReq = null;
            System.Net.FtpWebResponse ftpRes = null;
            Uri uri = null;

            public string adr = "ftp://www.xxxxxx.com/abc/pdf";  こんな感じ
            public string usr = "ユーザー名";
            public string pwl = "パスワード";

    開発環境 Windows10 VS2015 C# WinForm

    アップロード先 レンタルサーバー(Windows Server 2016 Datacenter) OSビルド 1607

    2019年10月15日 2:58

回答

  • あんまり真面目には読んでいませんが、とりあえずFtpWebRequestは何らかの要求を行うたびにCreateしてください。一度GetRequestしたFtpWebRequestインスタンスでもう一度GetResponseしてはいけません。
    • 編集済み Hongliang 2019年10月15日 3:42
    • 回答としてマーク ferret001 2019年10月16日 4:44
    2019年10月15日 3:15
  • 補足すると、

    FTPサーバーとのTCPコネクションは、FtpWebRequestオブジェクトが保持・管理しているわけではありません。FtpWebRequest.ServicePointに設定されているServicePointオブジェクトであり、ServicePointManagerクラスが管理しています。
    # なので、FtpWebRequestはIDisposableとなっていません。

    ですので、FtpWebRequestは毎回作成し、インスタンスは使い捨ててください。

    • 回答としてマーク ferret001 2019年10月16日 4:44
    2019年10月15日 3:35

すべての返信

  • あんまり真面目には読んでいませんが、とりあえずFtpWebRequestは何らかの要求を行うたびにCreateしてください。一度GetRequestしたFtpWebRequestインスタンスでもう一度GetResponseしてはいけません。
    • 編集済み Hongliang 2019年10月15日 3:42
    • 回答としてマーク ferret001 2019年10月16日 4:44
    2019年10月15日 3:15
  • 補足すると、

    FTPサーバーとのTCPコネクションは、FtpWebRequestオブジェクトが保持・管理しているわけではありません。FtpWebRequest.ServicePointに設定されているServicePointオブジェクトであり、ServicePointManagerクラスが管理しています。
    # なので、FtpWebRequestはIDisposableとなっていません。

    ですので、FtpWebRequestは毎回作成し、インスタンスは使い捨ててください。

    • 回答としてマーク ferret001 2019年10月16日 4:44
    2019年10月15日 3:35
  • お世話になります。

    > ですので、FtpWebRequestは毎回作成し、インスタンスは使い捨ててください

    毎回作成するとのことですので、ループ内でインスタンスを作成するようにしましたが、依然同じところで同じエラーになります。

    下記では毎回作成していることにならないのでしょうか?

          foreach (var txt in zipF)
          {
               if (txt.Length > 0)
               {
                    Ftp ftp = new Ftp();
                    ftp.adr = Form1._form1Instance.FtpServer;
                    ftp.usr = Form1._form1Instance.FtpUser;
                    ftp.pwl = Form1._form1Instance.FtpPassword;

    2019年10月15日 4:57
  • ftp.FtpExistDirectory(ftpFolder) の時点で ftpReq は使用済みです。 ftp.FtpMakeDirectory(ftpFolder) の前にftpインスタンスを再作成する必要があります。

    Ftpクラスの設計が根本的に間違っていることになります。

    2019年10月15日 6:36
  • 結論から言って連続でアップロードできるようになりました。

    接続できない理由はうまく説明できませんが、

    ftpReq.KeepAlive = false; を true に変更することで連続のアップロードが可能になりました。

    もう一つエラーが吐かれたときに意味が分からず、

         catch (WebException ex)
         {
               FtpWebResponse res = (FtpWebResponse)ex.Response;
               string txt = res.StatusDescription;             // 550 The system cannot find the path specified.
               MessageBox.Show(ex.Message);
          }

    とした方が、メッセージがより分かりやすいことも

    ありがとうございました。

    2019年10月16日 4:44
  • 相対パスと絶対パスの違いは理解されていますでしょうか? FTPに接続し、PWDコマンドで返される初期パス(仮に /xxx とします。)、URIが "ftp://www.example.com/abc/pdf" とした場合、絶対パスは /xxx/abc/pdf となります。これをURIで表すには "ftp://www.example.com/%2Fxxx/abc/pdf" と記述する必要があります。
    逆に相対パスのままですと、2回目の接続時に /xxx/abc/pdf/abc/pdf にアクセスしてしまう場合があり、「550 The system cannot find the path specified.」が発生してもおかしくありません。
    • 編集済み 佐祐理 2019年10月16日 8:08 コマンド誤りを修正
    2019年10月16日 8:06