この記事は、私が http / https の URL と FTP の URL を混同してハマってしまった恥ずかしいお話です。
FTP のアップロードでエラーが出た
System.NET の WebClient や FtpWebRequest を使って、Web サーバーに FTP でファイルをアップロードするコードを書いたんですヨ。
別に珍しくもない、よくある処理である。簡単であろう。楽勝である。と、思った。
コードを書き終えて、自信に満ち満ちて Visual Studio 上で F5 を押した。すると・・・
System.Net.WebException
HResult=0x80131509
Message=リモート サーバーがエラーを返しました: (550) ファイルが使用できません (例: ファイルが見つからない、ファイルへのアクセスがない)
こんなエラーが。
んなバナナ。
スペルミスはない。パスは正しいように思える
アップロード先のサーバーのパスを間違えたか?
パスのどこかにスペルミスがあるのか?
ディスプレイが照れて赤くなるのではないかと思うくらい画面を凝視してチェックしてみたが、パスに間違いはなさそう。スペルミスもないゾ、と。
コードをいろいろいじって、やり方を変えて何度も試したけれど、同じエラー(または類似のエラー)が出る。
ファイルをアップロードしたいディレクトリは https://elleneast.com/Tools のディレクトリ。Web ブラウザからはこの URL でアクセスできる。
実はこの URL からファイルを取得するプログラムを以前作ったことがあった。その動作に問題が無かったこともあり、(FTP の場合と混同して)FTP の場合もその URL が正しいと思い込んでしまう一因になった。
愛用の FTP ツール FileZilla でサーバーに接続したときは下図のように表示されている。
だから最初はアップロード先として ftp://elleneast.com/Tools/UploadedFile.txt を試したんだけど、エラーが出たので ftp://elleneast.com/public_html/Tools/UploadedFile.txt でも試してみた。けど、やっぱりエラーになった。
ftp://elleneast.com//xxx または ftp://elleneast.com/%2fxxx を試す
ftp://elleneast.com/xxx という(一般的な)指定をすると、これは相対パスを意味するので、FTP サーバーにログインした後(.NET Framework は)カレントディレクトリを
<ログイン後の初期ディレクトリとして設定してある場所>/xxx
に移すらしい。
一方、ftp://elleneast.com//xxx まはた ftp://elleneast.com/%2fxxx(%2f は “/” のエスケープ表現)と指定すると、これは絶対パスを意味するので、カレントディレクトリは /xxx になるらしい。
(参考:Microsoft – FtpWebRequest Class)
この挙動が原因でエラーになることもあるので、相対パスで指定してエラーになる場合は絶対パスに直して試してみると解決することもあるとか。
私の場合はこれでは解決しなかった。
FtpWebResponse.StatusDescription を調べるのも手
ちなみに、FTP 関連の例外エラーは .NET Framework が汎用的なメッセージに置き換えている場合があるので、下記のように FtpWebResponse.StatusDescription の内容を調べると、もっと根本的なエラーの原因が分かる場合があるらしい。
Dim oStream As System.IO.Stream = Nothing Try oStream = oFtpWebReq.GetRequestStream() Catch ex As WebException MsgBox(DirectCast(ex.Response, FtpWebResponse).StatusDescription) End Try
たとえば、このやり方で同じエラーを補足した場合、「550 elleneast.com/xxxxx: No such file or directory」というエラーメッセージを確認できました。
(参考:FTPでエラーが発生した場合に生のメッセージを取得するには?[2.0のみ、C#、VB])
Windows 標準の ftp コマンドで分かった真実
で、ふと思い出した。
遠い昔、コマンドプロンプトから Windows 標準搭載の FTP コマンドを使ったことがあったっけ。
あれって、サーバーに接続、ディレクトリ変更、ファイルのアップロード・・・といったコマンドの1つ1つを手動で実行していくから、現在どういう状態なのか分かりやすいんだよね、と思い出した。
よし、あれで試してみよう。何か分かるかもしれない。
Windows 標準の ftp で使える主な・・・というか最低限のコマンドは次の通りです。
open | 指定したサーバに接続する。 |
ls | サーバのファイルとディレクトリの一覧を表示する。”ls -l” と入力すると詳細を表示する。 |
cd |
ディレクトリを変更する。たとえば “cd Tools” と入力すると Tools ディレクトリに移動する。”cd ..” と入力すると1つ上のディレクトリに移動する。 |
bye | 接続を切断して ftp を終了する。 |
? | ftp コマンドの説明を表示する。たとえば “? ls” と入力すると ls コマンドの説明を表示する。 |
で、コマンドプロンプトを開いて、ftp と入力。プロンプトが ftp> に変わる。
続いてサーバーに接続するために open elleneast.com と入力。ユーザー名とパスワードを入力すると無事ログインできた。
いま elleneast.com を開いたわけだから、ここでファイル一覧を表示させれば Tools ディレクトリがあるはず。と思って ls を実行。「うむ。やっぱりあった」と思って Tools ディレクトリに入ろうと cd Tools を実行すると・・・あれ?!
「No such file or directory.」「そんなファイルとかディレクトリとかねーよ」って・・・?
ちゃんと Tools ディレクトリが表示されてるのに変だなと思いつつ、詳細表示してみようと思って ls -l と入力。すると・・・
Tools ディレクトリ・・・だとばかり思ってたのに、行頭にディレクトリを示す d がありません。これディレクトリじゃないゾ!と気付いたわけです。
ls -l コマンドで詳細表示をした場合、1文字目が d ならディレクトリ、l ならシンボリックリンク、- なら普通のファイルであることを意味します。
どうりで、アップロード先を ftp://elleneast.com/Tools/UploadFile.txt として試したコードがエラーになったわけだ。と納得。
じゃぁどこに本物の Tools ディレクトリがあるんだろうと cd, ls を繰り返して見つけた場所がここ。
最初に開いた場所/elleneast.com/public_html/Tools
それって、つまり、最初に開いた場所が elleneast.com だから、
elleneast.com/elleneast.com/public_html/Tools
ってことになるわけ? elleneast.com がだぶっちゃいますヨ??
ご冗談を ( ´艸`) と思いつつ・・・念のためアップロード先の URL を下記に修正して自作コードによる FTP アップロードを試したら・・・
ftp://elleneast.com/elleneast.com/public_html/Tools/someFile.txt
・・・転送成功!!
ま~~ぢ~~~か~~~~~。
当サイトの FTP URL の実態がこんな変ちくりんな状態になっているとは知らなかった。
と言っても最初の elleneast.com は本物のサーバー名のエイリアスなので、下記の URL でも自作コードによる FTP アップロードは成功した。
ftp://本物のサーバー名/elleneast.com/public_html/Tools/someFile.txt
ん~、そういうことか。
結論
ブラウザ(http / https)の URL をそのまま FTP の URL に転用できないことがあるので注意しましょう。
あったりまえじゃん
という声が聞こえてきそうです。
お恥ずかしい。(*ノωノ)
http / https の URL は実体とは異なるエイリアス(仮想ディレクトリ)を含んでいることがあるので、そのまま ftp の URL に使えるとは限らないわけですね(ftp は http / https のエイリアスを理解しない)。
そして、FTP で使用する正確なパスを(Windows 標準の ftp などで)きちんと調べるようにしましょう!
チャンチャン。
追記:FtpWebRequest 、WebClient、 WebRequest は使うな!
今回の件を調べているうちに知ったのですが、System.Net の FtpWebRequest クラス、WebClient クラス、WebRequest(HttpWebRequest 含む)クラスの使用は現在推奨されていないようです。
We don’t recommend that you use the FtpWebRequest class for new development.
For more information and alternatives to FtpWebRequest, see WebRequest shouldn’t be used on GitHub.
(引用:Microsoft – FtpWebRequest Class)
We don’t recommend that you use the WebClient class for new development. Instead, use the System.Net.Http.HttpClientclass.
(引用:Microsoft – WebClient Class)
We don’t recommend that you use WebRequest or its derived classes for new development. Instead, use the System.Net.Http.HttpClient class.
(引用:Microsoft – WebRequest Class)
WebRequest-based APIs are on life-support only (that is, only critical fixes, no new improvements, enhancements).
(引用:GitHub – WebRequest shouldn’t be used)
現在では
- WebClient のかわりに HttpClient の使用が推奨されています。
- WebRequest のかわりに HttpClient の使用が推奨されています。
- FtpWebRequest のかわりにサードパーティの FTP クライアントの使用が推奨されています。(例)