VB.NETでFTPダウンロードを実装する方法 [プログラミング]
VB.NETでFTPダウンロードを実装する方法について説明する。
ここで説明するFTPダウンロードは、FireWallサーバにログインした後、プロキシサーバを経由して目的のサーバに接続する形式である。
プロキシを経由しないFTP接続のサンプルはたくさんあるようなので、ここではプロキシ経由のサンプルを紹介する。
FTPクライアントにはSystem.Net.Sockets名前空間のSocketクラスを使用するしSystem.Net名前空間を多用するので、System.Netをインポートしておくと良い。
簡単に処理の流れは以下の通り。
1.Socketクラスのインスタンスの生成
2.FireWallサーバにログイン
3.プロキシサーバを経由して目的サーバにログイン
4.転送モード、送受信ポートの設定
5.ファイルのダウンロード
6.サーバとの接続を閉じる
では、その詳細を説明していこう。
1.Socketクラスのインスタンスの生成
Socketクラスのインスタンスの生成は「アドレスファミリ、ソケットタイプおよびプロトコル」を指定してインスタンスの初期化を行う。
アドレスファイミリは"InterNetwork"、ソケットタイプは"Stream"、そしてプロトコルは"Tcp"を指定する。
2.FireWallサーバにログイン
FireWallサーバにログインするための手順は以下の通り。
①IPEndPointクラス(System.Net名前空間)のインスタンスの生成
FireWallサーバに接続するためにIPEndPointクラスのインスタンスを生成して、接続先のホストとポートの情報を格納する。
IPアドレスにはDnsクラスのGetHostEntryメソッドにFireWallサーバの名前(名前解決ができていれば)またはIPアドレスを指定して実行した結果のAddressListプロパティの0番目要素を、ポート番号には21を指定してインスタンスの初期化を行う。
②FireWallサーバに接続
SocketクラスのインスタンスのConnectメソッドに先ほど生成したIPEndPointクラスのインスタンスを指定して実行してFireWallサーバに接続する。
③FireWallサーバにログイン
SocketクラスのインスタンスのSendメソッドに"USER [FireWallサーバの名前またはIPアドレス]"を指定して実行する。
※Sendメソッドの詳しい実装方法は後程説明する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
※Receiveメソッドの詳しい実装方法は後程説明する。
④FireWallユーザのパスワード送信
SocketクラスのインスタンスのSendメソッドに"PASS [FireWallユーザのパスワード]"を指定して実行する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
以上でFireWallサーバへのログインは完了である。
3.プロキシサーバを経由して目的サーバにログイン
プロキシサーバ経由で目的サーバにログインするための手順は以下の通り。
①目的サーバにログイン
SocketクラスのインスタンスのSendメソッドに"USER [ユーザ名]@[目的サーバの名前またはIPアドレス]@[プロキシサーバの名前またはIPアドレス]"を指定して実行する。
因みにプロキシサーバを経由しない場合は"USER [ユーザ名]@[目的サーバの名前またはIPアドレス]"を指定する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②目的サーバのログインパスワード送信
SocketクラスのインスタンスのSendメソッドに"PASS [目的サーバのログインパスワード]"を指定して実行する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
以上で目的サーバへのログインは完了である。
4.転送モード、送受信ポートの設定
転送モード、送受信ポートの設定手順は以下の通り。
①転送モードをASCIIに設定
SocketクラスのインスタンスのSendメソッドに"TYPE A"を指定して実行する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②クライアントのIPアドレスの取得
IPAddressクラス(System.Net名前空間)のインスタンスにDnsクラスのGetHostAddressesメソッドの実行結果の1番目要素を設定する。
DnsクラスのGetHostAddressesメソッドにはDnsクラスのGetHostNameメソッドの実行結果を指定する。
③Tcpリスナーの生成
TcpListenerクラス(System.Net.Sockets名前空間)のインスタンスを生成する。
IPアドレスには先ほど生成したIPAddressクラスのインスタンスを、ポートには0を指定して初期化する。
④Tcpリスナーの受信接続要求開始
TcpListenerクラスのインスタンスのStartメソッドを実行して受信接続要求の待機を開始する。
⑤クライアントの受信ポートの取得
TcpListenerクラスのインスタンスのLocalEndpointプロパティをIPEndPointに型変換し、そのPortプロパティの値を取得する。
⑥送受信ポートの設定
SocketクラスのインスタンスのSendメソッドに"PROT [IPAddressクラスのインスタンスを文字列変換した値の"."(ピリオド)を","(カンマ)に変換した値],[クライアント受信ポート番号÷256の商],[クライアント受信ポート番号÷256の剰余]"を指定して実行する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
以上で転送モード、送受信ポートの設定は完了である。
5.ファイルのダウンロード
ファイルのダウンロードの手順は以下の通り。
①ダウンロードを要求するファイルの送信
SocketクラスのインスタンスのSendメソッドに"RETR [ダウンロードするファイルのフルパス]"を指定して実行する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②ファイルのダウンロード
Socketクラス(System.Net.Sockets名前空間)のインスタンスを生成し、TcpListenerクラスのインスタンスのAcceptSocketプロパティの値を設定する。
FileStreamクラス(IO名前空間)のインスタンスを生成し、ダウンロード先ファイルのフルパスで初期化する。
受信データが0バイトになるまでSocketクラスのインスタンスのReceiveメソッドで取得したバイト型の配列の値をFileStreamクラスのインスタンスのWriteメソッドでファイルに出力する。
ファイルの受信が完了したらSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
③各インスタンスのクローズ
FileStreamクラスのインスタンスのCloseメソッドを実行してダウンロード先ファイルを閉じる。
SocketクラスのインスタンスのCloseメソッドを実行してファイル受信ソケットを閉じる。
TcpListenerクラスのインスタンスのStopメソッドを実行してTcpリスナーを閉じる。
以上でファイルのダウンロードは完了である。
6.サーバとの接続を閉じる
サーバとの接続終了の手順は以下の通り。
①サーバとの接続を閉じる
SocketクラスのインスタンスのSendメソッドに"QUIT"を指定して実行する。
続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②ソケット接続の終了
SocketクラスのインスタンスのShutdownメソッドを実行してソケットの送受信を終了する。
SocketクラスのインスタンスのCloseメソッドを実行してソケットの接続を閉じる。
上記の手順でプロキシサーバ経由のFTPダウンロードが行える。
もっとも、プロキシサーバ経由か否かは目的サーバへのログインが異なるだけ(そこが肝心な部分だが…)で、その他はプロキシ経由か否かに関係なく同じ手順である。
説明を保留していたSocketクラスのインスタンスのSendメソッドとReceiveメソッドについて以下に説明する。
1.Sendメソッド
SocketクラスのインスタンスのSendメソッドでコマンドを送信する手順は以下の通り。
①送信するコマンドをバイト列に変換
送信するコマンドをASCIIEncodingで初期化したEncodingクラス(Text名前空間)のインスタンスのGetBytesメソッドを実行して、ASCIIのバイト列に変換する。
②コマンドの送信
SocketクラスのインスタンスのSendメソッドに送信するコマンドのASCIIのバイト列とその長さを指定して実行する。
2.Receiveメソッド
SocketクラスのインスタンスのReceiveメソッドでサーバからの応答を受信する手順は以下の通り。
①サーバからの応答の受信
SocketクラスのインスタンスのAvailableプロパティの値>0の間、以下の処理を繰り返す。
SocketクラスのインスタンスのReceiveメソッドを実行してバイト列にデータを受信する。
受信したバイト列のデータをUTF8Encodingで初期化したEncodingクラス(Text名前空間)のインスタンスのGetStringメソッドを実行して、文字列に変換する。
以下は、上記FTPダウンロード手順のサンプルコード(抜粋)である。
フォームには以下のテキストボックスとボタンが配置されているものとする。
・FireWallサーバ名
・FireWallユーザ名
・FireWallパスワード
・プロキシサーバ名
・ホストサーバ名
・ホストサーバユーザ名
・ホストサーバパスワード
・ダウンロード元パス
・ダウンロード元ファイル名
・ダウンロード先パス
・ダウンロード先ファイル名
・実行ログ
・接続ボタン
フォームの各テキストボックスに値を入力し、接続ボタンを押下するとFTPダウンロードが始まり、サーバとの送受信内容が実行ログテキストボックスに出力される仕様である。
以下のサンプルではサーバへのコマンド送信後、サーバからの応答メッセージを受信する際にコマンドエラーのハンドリングを行っていないが、エラーハンドリングを行うのであれば受信データを文字列変換した後にエラーコードもしくはメッセージが含まれていないかをチェックすると良い。
因みにこのサンプルの接続先サーバはLinuxサーバである。
------------------------------------------------------------
Private gstrFireWallServer As String
Private gstrFireWallUser As String
Private gstrFireWallPassword As String
Private gstrHostProxy As String
Private gstrHostServer As String
Private gstrHostUser As String
Private gstrHostPassword As String
Private gstrHostPath As String
Private gstrHostFile As String
Private gstrClientPath As String
Private gstrClientFile As String
Private goClient As Sockets.Socket
Private goEncASC As Text.Encoding = New Text.ASCIIEncoding
Private goEncU8 As Text.Encoding = New Text.UTF8Encoding
Private Sub btnConnect_Click(sender As Object, e As System.EventArgs) Handles btnConnect.Click
'********************************
'接続ボタン押下時の処理
'********************************
'各種ユーザ入力情報の取得
gstrFireWallServer = txtFireWallServer.Text.Trim
gstrFireWallUser = txtFireWallUser.Text.Trim
gstrFireWallPassword = txtFireWallPassword.Text.Trim
gstrHostProxy = txtHostProxy.Text.Trim
gstrHostServer = txtHostServer.Text.Trim
gstrHostUser = txtHostUser.Text.Trim
gstrHostPassword = txtHostPassword.Text.Trim
gstrHostPath = txtHostPath.Text.Trim
gstrHostFile = txtHostFile.Text.Trim
gstrClientPath = txtClientPath.Text.Trim
gstrClientFile = txtClientFile.Text.Trim
'指定ファイルをFTPでサーバより取得
If fncGetFileForFtp() = False Then
Me.fncMessageput("ファイルの取得に失敗しました。", MsgBoxStyle.Exclamation)
Else
Me.fncMessageput("ファイルの取得が完了しました。", MsgBoxStyle.Information)
End If
End Sub
Private Function fncGetFileForFtp() As Boolean
'********************************
'FTPで指定サーバより指定ファイルをダウンロード
'
' 戻り値 : True:正常、False:異常
'
'********************************
Dim blnReturn As Boolean
Dim blnConnect As Boolean = False
Try
txtLog.Clear()
'ソケットオブジェクトの生成
goClient = New Sockets.Socket(Sockets.AddressFamily.InterNetwork, Sockets.SocketType.Stream, Sockets.ProtocolType.Tcp)
'ホスト(FireWall)の接続先とポートを生成
Dim objFwIp As IPEndPoint = New IPEndPoint(Dns.GetHostEntry(gstrFireWallServer).AddressList(0), 21)
'ホストに接続
goClient.Connect(objFwIp)
'FireWallにログイン
If fncSendCommand("USER " & gstrFireWallUser) = False Then
Exit Try
End If
If fncReceiveData() = False Then
Exit Try
End If
blnConnect = True
If fncSendCommand("PASS " & gstrFireWallPassword, gstrFireWallPassword) = False Then
Exit Try
End If
If fncReceiveData() = False Then
Exit Try
End If
'サーバにログイン
If fncSendCommand("USER " & gstrHostUser & "@" & gstrHostServer & "@" & gstrHostProxy) = False Then
Exit Try
End If
If fncReceiveData() = False Then
Exit Try
End If
If fncSendCommand("PASS " & gstrHostPassword, gstrHostPassword) = False Then
Exit Try
End If
If fncReceiveData() = False Then
Exit Try
End If
'転送モードをASCIIに設定
If fncSendCommand("TYPE A") = False Then
Exit Try
End If
If fncReceiveData() = False Then
Exit Try
End If
'クライアントのIPアドレスと受信ポートを設定
Dim objMyIp As IPAddress = Dns.GetHostAddresses(Dns.GetHostName())(1)
Dim objListener As Sockets.TcpListener = New Sockets.TcpListener(objMyIp, 0)
objListener.Start()
Dim intMyPort As Integer = CInt(DirectCast(objListener.LocalEndpoint, IPEndPoint).Port.ToString())
'送受信ポートを設定
If fncSendCommand("PORT " & objMyIp.ToString.Replace(".", ",") & "," & (intMyPort \ 256) & "," & (intMyPort Mod 256)) = False Then
Exit Try
End If
If fncReceiveData() = False Then
Exit Try
End If
'ダウンロードを要求するファイルを送信
If fncSendCommand("RETR " & gstrHostPath & "/" & gstrHostFile) = False Then
Exit Try
End If
If fncReceiveData() = False Then
Exit Try
End If
'ファイルをダウンロード
Dim objSoc As Sockets.Socket = objListener.AcceptSocket
Dim objStream As New IO.FileStream(IO.Path.Combine(gstrClientPath, gstrClientFile), IO.FileMode.Create, IO.FileAccess.Write)
Dim bytBuffer(1023) As Byte
Dim datStart As Date = Now()
Do
Dim intSize As Integer = objSoc.Receive(bytBuffer)
If intSize = 0 Then
Exit Do
End If
objStream.Write(bytBuffer, 0, intSize)
Loop
Dim datSpan As TimeSpan = Now() - datStart
If fncReceiveData() = False Then
Exit Try
End If
Dim strMsg As String = "ダウンロードは正常に終了しました。 (" & CInt(datSpan.TotalSeconds).ToString & "SEC. " & _
CInt(objStream.Length / datSpan.TotalSeconds).ToString & "B/S)"
Me.subPutLog(strMsg)
objStream.Close()
objSoc.Close()
objListener.Stop()
blnReturn = True
Catch ex As Exception
Me.fncMessageput(ex.Message, MsgBoxStyle.Exclamation)
blnReturn = False
Finally
If blnConnect = True Then
'接続を閉じる
Call fncSendCommand("QUIT")
Call fncReceiveData()
goClient.Shutdown(Sockets.SocketShutdown.Both)
goClient.Close()
End If
goClient.Dispose()
goClient = Nothing
End Try
Return blnReturn
End Function
Private Function fncReceiveData() As Boolean
'********************************
'サーバから応答メッセージを取得
'
' 戻り値 : True:正常、False:異常
'
'********************************
Dim blnReturn As Boolean
Dim bytData(255) As Byte
Dim intLen As Integer
Dim strData As String
Try
Do While goClient.Available > 0
'サーバからの応答を受信
intLen = goClient.Receive(bytData)
'受信したメッセージのバイト列を文字列に変換
strData = goEncU8.GetString(bytData, 0, intLen)
Me.subPutLog(strData)
Threading.Thread.Sleep(250)
Loop
blnReturn = True
Catch ex As Exception
Me.fncMessageput(ex.Message, MsgBoxStyle.Exclamation)
blnReturn = False
End Try
Return blnReturn
End Function
Private Function fncSendCommand(ByVal pCommand As String, Optional ByVal pPassword As String = "") As Boolean
'********************************
'サーバにコマンドを送信
'
' pCommand : 送信コマンド文字列
' pPassword : パスワード文字列
'
' 戻り値 : True:正常、False:異常
'
'********************************
Dim blnReturn As Boolean
Try
Me.subPutLog(">" & pCommand, pPassword)
'送信するコマンドをASCIIのバイト列に変換
pCommand = pCommand & ControlChars.CrLf
Dim bytCommand() As Byte = goEncASC.GetBytes(pCommand)
'コマンドを送信
goClient.Send(bytCommand, bytCommand.Length, Sockets.SocketFlags.None)
Threading.Thread.Sleep(250)
blnReturn = True
Catch ex As Exception
Me.fncMessageput(ex.Message, MsgBoxStyle.Exclamation)
blnReturn = False
End Try
Return blnReturn
End Function
------------------------------------------------------------
コメント 0