DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

FtpWebRequest、FtpWebResponseクラスを使ってFTPサーバーにアクセスする

注意:ここで紹介している方法は、.NET Framework 2.0以降でのみ使用できます。.NET Framework 1.1以前でFTPサーバーにアクセスする方法は、「[HOWTO] Visual Basic .NET を使用してプラグ可能なプロトコルを記述し、マネージ クラスで FTP をサポートする方法」や「Visual Basic .NET または Visual Basic 2005 を使用して FTP サイトにアクセスする方法」などをご覧ください。

FTPサーバーからファイルをダウンロードする

まずはRETRコマンドでFTPサーバーからファイルをダウンロードする方法を紹介します。

基本的には、「WebRequest、WebResponseクラスを使ってファイルをダウンロードし保存する」で紹介した方法と同じです。FtpWebRequestの場合、MethodプロパティにWebRequestMethods.Ftp.DownloadFileを指定することにより、ダウンロードすることができます(何も指定しなかった時も、WebRequestMethods.Ftp.DownloadFileを指定したのと同じになります)。

以下の例では、"ftp://localhost/test.txt"から"test.txt"をダウンロードして、"C:\test.txt"に保存しています。FTPサーバーへのログインユーザー名は"username"、パスワードは"password"であるものとします。

VB.NET
コードを隠すコードを選択
'ダウンロードするファイルのURI
Dim u As New Uri("ftp://localhost/test.txt")
'ダウンロードしたファイルの保存先
Dim downFile As String = "C:\test.txt"

'FtpWebRequestの作成
Dim ftpReq As System.Net.FtpWebRequest = _
    CType(System.Net.WebRequest.Create(u), System.Net.FtpWebRequest)
'ログインユーザー名とパスワードを設定
ftpReq.Credentials = New System.Net.NetworkCredential("username", "password")
'MethodにWebRequestMethods.Ftp.DownloadFile("RETR")を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.DownloadFile
'要求の完了後に接続を閉じる
ftpReq.KeepAlive = False
'ASCIIモードで転送する
ftpReq.UseBinary = False
'PASSIVEモードを無効にする
ftpReq.UsePassive = False

'FtpWebResponseを取得
Dim ftpRes As System.Net.FtpWebResponse = _
    CType(ftpReq.GetResponse(), System.Net.FtpWebResponse)
'ファイルをダウンロードするためのStreamを取得
Dim resStrm As System.IO.Stream = ftpRes.GetResponseStream()
'ダウンロードしたファイルを書き込むためのFileStreamを作成
Dim fs As New System.IO.FileStream( _
    downFile, System.IO.FileMode.Create, System.IO.FileAccess.Write)
'ダウンロードしたデータを書き込む
Dim buffer(1023) As Byte
While True
    Dim readSize As Integer = resStrm.Read(buffer, 0, buffer.Length)
    If readSize = 0 Then
        Exit While
    End If
    fs.Write(buffer, 0, readSize)
End While
fs.Close()
resStrm.Close()

'FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription)
'閉じる
ftpRes.Close()
C#
コードを隠すコードを選択
//ダウンロードするファイルのURI
Uri u = new Uri("ftp://localhost/test.txt");
//ダウンロードしたファイルの保存先
string downFile = "C:\\test.txt";

//FtpWebRequestの作成
System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
    System.Net.WebRequest.Create(u);
//ログインユーザー名とパスワードを設定
ftpReq.Credentials = new System.Net.NetworkCredential("username", "password");
//MethodにWebRequestMethods.Ftp.DownloadFile("RETR")を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
//要求の完了後に接続を閉じる
ftpReq.KeepAlive = false;
//ASCIIモードで転送する
ftpReq.UseBinary = false;
//PASSIVEモードを無効にする
ftpReq.UsePassive = false;

//FtpWebResponseを取得
System.Net.FtpWebResponse ftpRes =
    (System.Net.FtpWebResponse)ftpReq.GetResponse();
//ファイルをダウンロードするためのStreamを取得
System.IO.Stream resStrm = ftpRes.GetResponseStream();
//ダウンロードしたファイルを書き込むためのFileStreamを作成
System.IO.FileStream fs = new System.IO.FileStream(
    downFile, System.IO.FileMode.Create, System.IO.FileAccess.Write);
//ダウンロードしたデータを書き込む
byte[] buffer = new byte[1024];
while (true)
{
    int readSize = resStrm.Read(buffer, 0, buffer.Length);
    if (readSize == 0)
        break;
    fs.Write(buffer, 0, readSize);
}
fs.Close();
resStrm.Close();

//FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription);
//閉じる
ftpRes.Close();

上記の例では、FtpWebRequest.KeepAliveプロパティをfalseにしていますので、FtpWebResponseのCloseメソッドが呼び出された時に、ログアウトしてサーバーとの接続が切断されます。KeepAliveプロパティがtrueの時(デフォルト)は、FtpWebResponse.Closeメソッドが呼び出されてもログアウトせず、接続は継続されます。これについて詳しくは、後述します。

また、FtpWebRequest.UseBinaryをfalseにして、ASCIIモード("Type A"コマンド)で転送しています。デフォルトはtrueで、バイナリモード("TYPE I"コマンド)で転送されます。ASCIIモードでは、通常改行コードが変換されます(ファイルをダウンロードするときはLFをCR+LFに、アップロードするときはCR+LFをLFに変換)。バイナリモードでは、変換されません。ただし、サーバーがこのコマンドを無視する可能性もあります。UseBinaryのデフォルトはTrueです。

さらに、FtpWebRequest.UsePassiveをfalseにして、PASVモード(パッシブモード)を使わないようにしています。パッシブモードでは、データ転送用のポートをクライアント側が指定します。通常ファイアウォールの内側から外部のFTPサーバーに接続する場合は、パッシブモードにしないと接続できません。UsePassiveプロパティのデフォルトはtrueで、PASVモードになります。

なお、ダウンロードするファイルがFTPサーバーに存在しなかった場合は、FtpWebResponse.GetResponseメソッドを呼び出した時に、例外WebExceptionがスローされます。例外がスローされた時に原因を調べる方法は、後述します。

相対パスと絶対パス

上記の例では、URIを"ftp://localhost/test.txt"としていますが、このURIはFTPサーバーにログインした時のディレクトリからの相対パスになります。具体的には、例えばFTPサーバーにログインした時のディレクトリが"public_html"であったならば、"public_html"ディレクトリの"test.txt"がダウンロードされます。

絶対パスのURIにするには、"ftp://localhost/%2ftest.txt"とし、"/"をエスケープした"%2f"を使用します。このようにすると、ルートディレクトリにある"test.txt"がダウンロードされます。

FTPサーバーにファイルをアップロードする

"STOR"コマンドでFTPサーバーにファイルをアップロードするには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.UploadFileを指定します。"STOR"コマンドでは、同名のファイルがサーバーに存在すると、上書きされます。

アップロードするデータは、FtpWebRequest.GetRequestStreamメソッドで取得したストリームに書き込みます。これは、「POSTによりデータを送受信する」と同じです。

以下の例では、"C:\test.txt"を"ftp://localhost/test.txt"にアップロードしています。

VB.NET
コードを隠すコードを選択
'アップロードするファイル
Dim upFile As String = "C:\test.txt"
'アップロード先のURI
Dim u As New Uri("ftp://localhost/test.txt")

'FtpWebRequestの作成
Dim ftpReq As System.Net.FtpWebRequest = _
    CType(System.Net.WebRequest.Create(u), System.Net.FtpWebRequest)
'ログインユーザー名とパスワードを設定
ftpReq.Credentials = New System.Net.NetworkCredential("username", "password")
'MethodにWebRequestMethods.Ftp.UploadFile("STOR")を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.UploadFile
'要求の完了後に接続を閉じる
ftpReq.KeepAlive = False
'ASCIIモードで転送する
ftpReq.UseBinary = False
'PASVモードを無効にする
ftpReq.UsePassive = False

'ファイルをアップロードするためのStreamを取得
Dim reqStrm As System.IO.Stream = ftpReq.GetRequestStream()
'アップロードするファイルを開く
Dim fs As New System.IO.FileStream( _
    upFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)
'アップロードStreamに書き込む
Dim buffer(1023) As Byte
While True
    Dim readSize As Integer = fs.Read(buffer, 0, buffer.Length)
    If readSize = 0 Then
        Exit While
    End If
    reqStrm.Write(buffer, 0, readSize)
End While
fs.Close()
reqStrm.Close()

'FtpWebResponseを取得
Dim ftpRes As System.Net.FtpWebResponse = _
    CType(ftpReq.GetResponse(), System.Net.FtpWebResponse)
'FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription)
'閉じる
ftpRes.Close()
C#
コードを隠すコードを選択
//アップロードするファイル
string upFile = "C:\\test.txt";
//アップロード先のURI
Uri u = new Uri("ftp://localhost/test.txt");

//FtpWebRequestの作成
System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
    System.Net.WebRequest.Create(u);
//ログインユーザー名とパスワードを設定
ftpReq.Credentials = new System.Net.NetworkCredential("username", "password");
//MethodにWebRequestMethods.Ftp.UploadFile("STOR")を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.UploadFile;
//要求の完了後に接続を閉じる
ftpReq.KeepAlive = false;
//ASCIIモードで転送する
ftpReq.UseBinary = false;
//PASVモードを無効にする
ftpReq.UsePassive = false;

//ファイルをアップロードするためのStreamを取得
System.IO.Stream reqStrm = ftpReq.GetRequestStream();
//アップロードするファイルを開く
System.IO.FileStream fs = new System.IO.FileStream(
    upFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
//アップロードStreamに書き込む
byte[] buffer = new byte[1024];
while (true)
{
    int readSize = fs.Read(buffer, 0, buffer.Length);
    if (readSize == 0)
        break;
    reqStrm.Write(buffer, 0, readSize);
}
fs.Close();
reqStrm.Close();

//FtpWebResponseを取得
System.Net.FtpWebResponse ftpRes =
    (System.Net.FtpWebResponse)ftpReq.GetResponse();
//FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription);
//閉じる
ftpRes.Close();

STOUコマンドでFTPサーバーにファイルをアップロードする

FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.UploadFileWithUniqueNameを指定すると、"STOU"コマンドでファイルをアップロードすることができます。"STOR"コマンドと違い、FTPサーバーに同名のファイルが存在していても上書きはせずに、サーバーがユニークなファイル名をつけます。

方法は、上記の「FTPサーバーにファイルをアップロードする」と同じで、"WebRequestMethods.Ftp.UploadFile"を"WebRequestMethods.Ftp.UploadFileWithUniqueName"に変更するだけです。

補足:STOUコマンドをサポートしていないサーバーでは、FtpWebResponse.GetResponseメソッドを呼び出した時に、例外WebExceptionがスローされます。

追加書き込みでファイルをアップロードする

FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.AppendFileを指定することにより、"APPE"コマンドでファイルをアップロードすることができます。この場合、FTPサーバーに同名のファイルが存在すると、そのファイルに追加書き込みで書き込まれます。同名のファイルが存在しないならば、新しくファイルが作成されます。

方法は、上記の「FTPサーバーにファイルをアップロードする」と同じで、"WebRequestMethods.Ftp.UploadFile"を"WebRequestMethods.Ftp.AppendFile"に変更するだけです。

FTPサーバーのディレクトリのファイル一覧を取得する

FTPサーバーにあるディレクトリ内のファイル一覧を取得するには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.ListDirectoryまたはListDirectoryDetailsを指定します。ListDirectoryを指定した場合は、"NLIST"コマンドにより、簡単なファイルの一覧(ファイルとディレクトリ名が改行文字で区切られた文字列)が返されます。ListDirectoryDetailsを指定した場合は、"LIST"コマンドにより、より詳細なファイルの一覧(ファイルやディレクトリの属性、所有者、更新日時、サイズなどを含む)が返されます。

以下の例では、"LIST"コマンドで"ftp://localhost/"のファイル一覧を取得して、表示しています。

VB.NET
コードを隠すコードを選択
'ファイル一覧を取得するディレクトリのURI
Dim u As New Uri("ftp://localhost/")

'FtpWebRequestの作成
Dim ftpReq As System.Net.FtpWebRequest = _
    CType(System.Net.WebRequest.Create(u), System.Net.FtpWebRequest)
'ログインユーザー名とパスワードを設定
ftpReq.Credentials = New System.Net.NetworkCredential("username", "password")
'MethodにWebRequestMethods.Ftp.ListDirectoryDetails("LIST")を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.ListDirectoryDetails
'要求の完了後に接続を閉じる
ftpReq.KeepAlive = False
'PASSIVEモードを無効にする
ftpReq.UsePassive = False

'FtpWebResponseを取得
Dim ftpRes As System.Net.FtpWebResponse = _
    CType(ftpReq.GetResponse(), System.Net.FtpWebResponse)
'FTPサーバーから送信されたデータを取得
Dim sr As New System.IO.StreamReader(ftpRes.GetResponseStream())
Dim res As String = sr.ReadToEnd()
'ファイル一覧を表示
Console.WriteLine(res)
sr.Close()

'FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription)
'閉じる
ftpRes.Close()
C#
コードを隠すコードを選択
//ファイル一覧を取得するディレクトリのURI
Uri u = new Uri("ftp://localhost/");

//FtpWebRequestの作成
System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
    System.Net.WebRequest.Create(u);
//ログインユーザー名とパスワードを設定
ftpReq.Credentials = new System.Net.NetworkCredential("username", "password");
//MethodにWebRequestMethods.Ftp.ListDirectoryDetails("LIST")を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.ListDirectoryDetails;
//要求の完了後に接続を閉じる
ftpReq.KeepAlive = false;
//PASSIVEモードを無効にする
ftpReq.UsePassive = false;

//FtpWebResponseを取得
System.Net.FtpWebResponse ftpRes =
    (System.Net.FtpWebResponse)ftpReq.GetResponse();
//FTPサーバーから送信されたデータを取得
System.IO.StreamReader sr =
    new System.IO.StreamReader(ftpRes.GetResponseStream());
string res = sr.ReadToEnd();
//ファイル一覧を表示
Console.WriteLine(res);
sr.Close();

//FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription);
//閉じる
ftpRes.Close();

上記の例で日本語が文字化けする場合は、StreamReaderオブジェクトを作成するときに、適当なEncodingを指定してください。(FtpWebRequestはサーバーに接続後、"OPTS utf8 on"を送信していますので、サーバーがUTF-8をサポートしていれば、このままで問題ないでしょう。)

なお、このように"LIST"コマンドで取得したファイル一覧を解析する方法は、「Sample code for parsing FtpwebRequest response for ListDirectoryDetails」や「FTP のプロトコルの取得方法について」で紹介されています。

FTPサーバーのファイルの名前を変更する

FTPサーバーにあるファイルの名前を変更するには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.Renameを指定します。このようにすると、"RNFR"と"RNTO"コマンドにより、ファイル名を変更します。変更後のファイル名は、FtpWebRequest.RenameToプロパティに設定しておきます。

以下の例では、"ftp://localhost/test.txt"のファイル名を"test2.txt"に変更しています。

VB.NET
コードを隠すコードを選択
'名前を変更するファイルのURI
Dim u As New Uri("ftp://localhost/test.txt")
'新しいファイル名
Dim newName As String = "test2.txt"

'FtpWebRequestの作成
Dim ftpReq As System.Net.FtpWebRequest = _
    CType(System.Net.WebRequest.Create(u), System.Net.FtpWebRequest)
'ログインユーザー名とパスワードを設定
ftpReq.Credentials = New System.Net.NetworkCredential("username", "password")
'MethodにWebRequestMethods.Ftp.Rename(RENAME)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.Rename

'変更後の新しいファイル名を設定
ftpReq.RenameTo = newName

'FtpWebResponseを取得
Dim ftpRes As System.Net.FtpWebResponse = _
    CType(ftpReq.GetResponse(), System.Net.FtpWebResponse)

'FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription)
'閉じる
ftpRes.Close()
C#
コードを隠すコードを選択
//名前を変更するファイルのURI
Uri u = new Uri("ftp://localhost/test.txt");
//新しいファイル名
string newName = "test2.txt";

//FtpWebRequestの作成
System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
    System.Net.WebRequest.Create(u);
//ログインユーザー名とパスワードを設定
ftpReq.Credentials = new System.Net.NetworkCredential("username", "password");
//MethodにWebRequestMethods.Ftp.Rename(RENAME)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.Rename;

//変更後の新しいファイル名を設定
ftpReq.RenameTo = newName;

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

//FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription);
//閉じる
ftpRes.Close();

FTPサーバーのファイルやディレクトリを削除する

"DELE"コマンドを送信してFTPサーバーにあるファイルを削除するには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.DeleteFileを指定します。"RMD"コマンドを送信してディレクトリを削除する時は、MethodプロパティにWebRequestMethods.Ftp.RemoveDirectoryを指定します。

以下の例では、"ftp://localhost/test.txt"を削除しています。

VB.NET
コードを隠すコードを選択
'削除するファイルのURI
Dim u As New Uri("ftp://localhost/test.txt")

'FtpWebRequestの作成
Dim ftpReq As System.Net.FtpWebRequest = _
    CType(System.Net.WebRequest.Create(u), System.Net.FtpWebRequest)
'ログインユーザー名とパスワードを設定
ftpReq.Credentials = New System.Net.NetworkCredential("username", "password")
'MethodにWebRequestMethods.Ftp.DeleteFile(DELE)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.DeleteFile

'FtpWebResponseを取得
Dim ftpRes As System.Net.FtpWebResponse = _
    CType(ftpReq.GetResponse(), System.Net.FtpWebResponse)

'FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription)
'閉じる
ftpRes.Close()
C#
コードを隠すコードを選択
//削除するファイルのURI
Uri u = new Uri("ftp://localhost/test.txt");

//FtpWebRequestの作成
System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
    System.Net.WebRequest.Create(u);
//ログインユーザー名とパスワードを設定
ftpReq.Credentials = new System.Net.NetworkCredential("username", "password");
//MethodにWebRequestMethods.Ftp.DeleteFile(DELE)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.DeleteFile;

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

//FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription);
//閉じる
ftpRes.Close();

FTPサーバーにディレクトリを作成する

"MKD"コマンドを送信してFTPサーバーにディレクトリを作成するには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.MakeDirectoryを指定します。方法は、「FTPサーバーのファイルやディレクトリを削除する」と同じです。

FTPサーバーのファイルのサイズを取得する

"SIZE"コマンドを送信してFTPサーバーにあるファイルのサイズを取得するには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.GetFileSizeを指定します。取得したファイルサイズは、FtpWebResponse.ContentLengthプロパティに格納されます。

VB.NET
コードを隠すコードを選択
'サイズを取得するファイルのURI
Dim u As New Uri("ftp://localhost/test.txt")

'FtpWebRequestの作成
Dim ftpReq As System.Net.FtpWebRequest = _
    CType(System.Net.WebRequest.Create(u), System.Net.FtpWebRequest)
'ログインユーザー名とパスワードを設定
ftpReq.Credentials = New System.Net.NetworkCredential("username", "password")
'MethodにWebRequestMethods.Ftp.GetFileSize(SIZE)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.GetFileSize

'FtpWebResponseを取得
Dim ftpRes As System.Net.FtpWebResponse = _
    CType(ftpReq.GetResponse(), System.Net.FtpWebResponse)
'取得したファイルサイズを表示
Console.WriteLine(ftpRes.ContentLength)

'FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription)
'閉じる
ftpRes.Close()
C#
コードを隠すコードを選択
//サイズを取得するファイルのURI
Uri u = new Uri("ftp://localhost/test.txt");

//FtpWebRequestの作成
System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
    System.Net.WebRequest.Create(u);
//ログインユーザー名とパスワードを設定
ftpReq.Credentials = new System.Net.NetworkCredential("username", "password");
//MethodにWebRequestMethods.Ftp.GetFileSize(SIZE)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.GetFileSize;

//FtpWebResponseを取得
System.Net.FtpWebResponse ftpRes =
    (System.Net.FtpWebResponse)ftpReq.GetResponse();
//取得したファイルサイズを表示
Console.WriteLine(ftpRes.ContentLength);

//FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription);
//閉じる
ftpRes.Close();

FTPサーバーのファイルのファイルの更新日時を取得する

"MDTM"コマンドを送信してFTPサーバーにあるファイルの更新日時を取得するには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.GetDateTimestampを指定します。取得したファイルの更新日時は、FtpWebResponse.LastModifiedプロパティに格納されます。

VB.NET
コードを隠すコードを選択
'最終更新日時を取得するファイルのURI
Dim u As New Uri("ftp://localhost/test.txt")

'FtpWebRequestの作成
Dim ftpReq As System.Net.FtpWebRequest = _
    CType(System.Net.WebRequest.Create(u), System.Net.FtpWebRequest)
'ログインユーザー名とパスワードを設定
ftpReq.Credentials = New System.Net.NetworkCredential("username", "password")
'MethodにWebRequestMethods.Ftp.GetDateTimestamp(MDTM)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.GetDateTimestamp

'FtpWebResponseを取得
Dim ftpRes As System.Net.FtpWebResponse = _
    CType(ftpReq.GetResponse(), System.Net.FtpWebResponse)
'最終更新日時を表示
Console.WriteLine(ftpRes.LastModified)

'FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription)
'閉じる
ftpRes.Close()
C#
コードを隠すコードを選択
//最終更新日時を取得するファイルのURI
Uri u = new Uri("ftp://localhost/test.txt");

//FtpWebRequestの作成
System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
    System.Net.WebRequest.Create(u);
//ログインユーザー名とパスワードを設定
ftpReq.Credentials = new System.Net.NetworkCredential("username", "password");
//MethodにWebRequestMethods.Ftp.GetDateTimestamp(MDTM)を設定
ftpReq.Method = System.Net.WebRequestMethods.Ftp.GetDateTimestamp;

//FtpWebResponseを取得
System.Net.FtpWebResponse ftpRes =
    (System.Net.FtpWebResponse)ftpReq.GetResponse();
//最終更新日時を表示
Console.WriteLine(ftpRes.LastModified);

//FTPサーバーから送信されたステータスを表示
Console.WriteLine("{0}: {1}", ftpRes.StatusCode, ftpRes.StatusDescription);
//閉じる
ftpRes.Close();

FTPサーバーのカレントディレクトリを取得する

"PWD"コマンドを送信してFTPサーバーのカレントディレクトリを取得するには、FtpWebRequestのMethodプロパティにWebRequestMethods.Ftp.PrintWorkingDirectoryを指定します。カレントディレクトリの情報は、FtpWebResponse.StatusDescriptionプロパティで取得できるメッセージ内に含まれます。方法は、「FTPサーバーのファイルやディレクトリを削除する」と同じですので、ここでは省略します。

WebRequestMethods.Ftpのメンバ以外のコマンドについて

FtpWebRequestのMethodプロパティには、WebRequestMethods.Ftpで定義されているパブリックフィールド以外の値は設定できません。よって、例えばSITEコマンドは送信できず、"SITE CHMOD"でファイルのパーミッション(属性)を変更することができません。

FtpWebRequestで送信することのできないコマンドをどうしても送信したい場合は、FtpWebRequestクラスを使用せずに、TcpClientやSocketクラスを使用してFTPサーバーに接続します。例えば、TcpClientクラスでFTPサーバーのファイルのパーミッションを変更するコードは、こちらなどで紹介されています。

補足:サードパーティーの無料(LGPL)のライブラリであるFree .NET FTP componentを使えば、SITEコマンドの送信が可能のようです。

一度の接続で複数のコマンドを送信する

例えば、FTPサーバーからファイル一覧を取得して、目的のファイルが存在すればそのファイルをダウンロードしたいといったケースは少なくありません。しかし、FtpWebRequestオブジェクトのMethodプロパティは、GetRequestStreamやGetResponseメソッドを呼び出した後には変更することができませんので、一つのFtpWebRequestオブジェクトを使って、あるコマンドを送信した後に別のコマンドを送信することができません。よって複数のコマンドを送信したい場合は、その数だけFtpWebRequestオブジェクトを作成する必要があります。

複数のFtpWebRequestオブジェクトを作成してコマンドを送信するとなると、それだけFTPサーバーとの接続と切断を繰り返すように思われるかもしれませんが、そうではありません。FtpWebRequestのKeepAliveプロパティがTrue(デフォルト)になっていれば、GetResponse.Closeメソッドが呼び出されてもFTPサーバーとの接続は切断されず、その後別のFtpWebRequestオブジェクトがその接続を利用することができます。デフォルトでKeepAliveプロパティはTrueです。

FTPサーバーから切断するときは、コマンドを送信するときにKeepAliveプロパティをFalseにします。

一つ例を示しましょう。以下のコードでは、Button1ボタンを押すと"ftp://localhost/doc"ディレクトリ内の".txt"ファイルをすべて削除しており、"Ftp.ListDirectory"を送信してから"Ftp.DeleteFile"を複数回送信しています。複数のFtpWebRequestオブジェクトを作成してコマンドを送信していますが、その間FTPサーバーにログインしたままログアウトすることなく、すべての作業が行われます。

さらにすべての作業終了後、DisconnectFtpメソッドを呼び出してFTPサーバーから切断しています。DisconnectFtpメソッドでは、KeepAliveプロパティをFalseにして"Ftp.PrintWorkingDirectory"コマンドを送信しています。切断するためにBYEコマンドだけ送信できればよいのですが、それができないため、仕方なくPrintWorkingDirectoryを送信しています。

VB.NET
コードを隠すコードを選択
Private ftpCredential As System.Net.NetworkCredential

''' <summary> 
''' FTPサーバーからファイル一覧を取得する 
''' </summary> 
''' <param name="dirUri">ディレクトリのURI</param> 
''' <returns>ファイル、ディレクトリ名の一覧</returns> 
Private Function GetFtpDirectoryList(ByVal dirUri As String) As String()
    Dim ftpReq As System.Net.FtpWebRequest = _
        DirectCast(System.Net.WebRequest.Create(dirUri), System.Net.FtpWebRequest)
    If Me.ftpCredential IsNot Nothing Then
        ftpReq.Credentials = Me.ftpCredential
    End If
    ftpReq.Method = System.Net.WebRequestMethods.Ftp.ListDirectory
    ftpReq.UsePassive = False

    Dim dirList As New System.Collections.Generic.List(Of String)()
    Using ftpRes As System.Net.FtpWebResponse = _
        DirectCast(ftpReq.GetResponse(), System.Net.FtpWebResponse)
        Using sr As New System.IO.StreamReader(ftpRes.GetResponseStream())
            While True
                Dim line As String = sr.ReadLine()
                If line Is Nothing Then
                    Exit While
                End If
                dirList.Add(line)
            End While
        End Using
    End Using

    Return dirList.ToArray()
End Function

''' <summary> 
''' FTPサーバーのファイルを削除する 
''' </summary> 
''' <param name="fileUri">削除するファイルのURI</param> 
Private Sub DeleteFtpFile(ByVal fileUri As String)
    Dim ftpReq As System.Net.FtpWebRequest = _
        DirectCast(System.Net.WebRequest.Create(fileUri), System.Net.FtpWebRequest)
    If Me.ftpCredential IsNot Nothing Then
        ftpReq.Credentials = Me.ftpCredential
    End If
    ftpReq.Method = System.Net.WebRequestMethods.Ftp.DeleteFile

    Using ftpRes As System.Net.FtpWebResponse = _
        DirectCast(ftpReq.GetResponse(), System.Net.FtpWebResponse)
    End Using
End Sub

''' <summary> 
''' FTPサーバーから切断する 
''' </summary> 
''' <param name="uri">切断するFTPサーバーのURI</param> 
Private Sub DisconnectFtp(ByVal uri As String)
    Dim ftpReq As System.Net.FtpWebRequest = _
        DirectCast(System.Net.WebRequest.Create(uri), System.Net.FtpWebRequest)
    If Me.ftpCredential IsNot Nothing Then
        ftpReq.Credentials = Me.ftpCredential
    End If
    ftpReq.Method = System.Net.WebRequestMethods.Ftp.PrintWorkingDirectory
    ftpReq.KeepAlive = False

    Using ftpRes As System.Net.FtpWebResponse = _
        DirectCast(ftpReq.GetResponse(), System.Net.FtpWebResponse)
    End Using
End Sub

'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
    Handles Button1.Click

    Me.ftpCredential = New System.Net.NetworkCredential("username", "password")
    Dim dirList As String() = Me.GetFtpDirectoryList("ftp://localhost/doc/")

    Dim fn As String
    For Each fn In dirList
        If fn.EndsWith(".txt", StringComparison.CurrentCultureIgnoreCase) Then
            Me.DeleteFtpFile(("ftp://localhost/doc/" + fn))
        End If
    Next fn

    Me.DisconnectFtp("ftp://localhost")
End Sub
C#
コードを隠すコードを選択
private System.Net.NetworkCredential ftpCredential;

/// <summary>
/// FTPサーバーからファイル一覧を取得する
/// </summary>
/// <param name="dirUri">ディレクトリのURI</param>
/// <returns>ファイル、ディレクトリ名の一覧</returns>
private string[] GetFtpDirectoryList(string dirUri)
{
    System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
        System.Net.WebRequest.Create(dirUri);
    if (this.ftpCredential != null)
        ftpReq.Credentials = this.ftpCredential;
    ftpReq.Method = System.Net.WebRequestMethods.Ftp.ListDirectory;
    ftpReq.UsePassive = false;

    System.Collections.Generic.List<string> dirList =
        new System.Collections.Generic.List<string>();
    using (System.Net.FtpWebResponse ftpRes =
        (System.Net.FtpWebResponse)ftpReq.GetResponse())
    using (System.IO.StreamReader sr =
        new System.IO.StreamReader(ftpRes.GetResponseStream()))
    {
        while (true)
        {
            string line = sr.ReadLine();
            if (line == null)
                break;
            dirList.Add(line);
        }
    }

    return dirList.ToArray();
}

/// <summary>
/// FTPサーバーのファイルを削除する
/// </summary>
/// <param name="fileUri">削除するファイルのURI</param>
private void DeleteFtpFile(string fileUri)
{
    System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
        System.Net.WebRequest.Create(fileUri);
    if (this.ftpCredential != null)
        ftpReq.Credentials = this.ftpCredential;
    ftpReq.Method = System.Net.WebRequestMethods.Ftp.DeleteFile;

    using (System.Net.FtpWebResponse ftpRes =
        (System.Net.FtpWebResponse)ftpReq.GetResponse())
    {
    }
}

/// <summary>
/// FTPサーバーから切断する
/// </summary>
/// <param name="uri">切断するFTPサーバーのURI</param>
private void DisconnectFtp(string uri)
{
    System.Net.FtpWebRequest ftpReq = (System.Net.FtpWebRequest)
        System.Net.WebRequest.Create(uri);
    if (this.ftpCredential != null)
        ftpReq.Credentials = this.ftpCredential;
    ftpReq.Method = System.Net.WebRequestMethods.Ftp.PrintWorkingDirectory;
    ftpReq.KeepAlive = false;

    using (System.Net.FtpWebResponse ftpRes =
        (System.Net.FtpWebResponse)ftpReq.GetResponse())
    {
    }
}

//Button1のClickイベントハンドラ
private void Button1_Click(object sender, EventArgs e)
{
    this.ftpCredential =
        new System.Net.NetworkCredential("username", "password");
    string[] dirList = this.GetFtpDirectoryList("ftp://localhost/doc/");

    foreach (string fn in dirList)
    {
        if (fn.EndsWith(".txt", StringComparison.CurrentCultureIgnoreCase))
        {
            this.DeleteFtpFile("ftp://localhost/doc/" + fn);
        }
    }

    this.DisconnectFtp("ftp://localhost");
}

このコードを実行したときに、具体的にFTPサーバーとどのようなやりとりが行われるかの例を以下に示します。これは、FTPサーバーとしてnekosogiFTPを使用した時に出力されたログを一部編集したものです。

>>220 Enter Your ID
>>USER username
<<331 Enter Your Password
>>PASS password
<<230 Login OK!!!
>>OPTS utf8 on
<<502 Command not implemented.
>>PWD
<<257 "/" is your directory.
>>CWD /doc/
<<250 Requested file action okay, completed.
>>TYPE I
<<200 Binary Mode
>>PORT 127,0,0,1,7,37
<<200 PORT command successful.
>>NLST
<<150 Opening data connection.
<<226 Transfer complete.
>>DELE 1.txt
<<250 Requested file action okay, completed.
>>DELE 2.txt
<<250 Requested file action okay, completed.
>>DELE 3.txt
<<250 Requested file action okay, completed.
>>CWD //
<<250 Requested file action okay, completed.
>>PWD
<<257 "/" is your directory.
>>QUIT
<<221 Good-Bye

例外がスローされた時は

例外がスローされてうまくいかないときは、まずスローされた例外の内容を確認してください。もしスローされた例外がWebExceptionであれば、WebExceptionのStatusDescriptionプロパティで最後にサーバーが返したステータスコードを確認できます。

例えば下図のような「WebExceptionはハンドルされませんでした。」というウィンドウが表示されたとします。

WebExceptionはハンドルされませんでした

ここで「アクション」の「詳細の表示...」をクリックすると、以下のようなダイアログが表示されて、WebExceptionの中身を確認できます。

詳細の表示

それでも原因が分からないときは、ネットワークトレースを行い、サーバーとのやり取りを確認してみてください。ネットワークトレースについては、「ネットワークのトレースを行う」をご覧ください。

  • 履歴:
  • 2009/10/3 「一度の接続で複数のコマンドを送信する」でサーバーから切断するメソッドを追加。「例外がスローされた時は」を追加。ASCIIモードやパッシブモードの説明を追加。
  • 2010/1/15 「一度の接続で複数のコマンドを送信する」にFTPサーバーのログを追加。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。