非同期操作でFTP転送するのはどうやるの?
FtpWebRequest クラス (System.Net)
の中の最後のサンプルを徹底解説してみるよ
このコードが何をやってるのかと言えば
非同期操作を使用して、FTP サーバーにファイルをアップロードするコード例を次に示します。
だそうです。
ふみゅ。
非同期操作とは
コマンドの実行など、データベースでの一部の操作は、完了までに長時間かかることがあります。 そのような場合、シングルスレッドのアプリケーションでは、他の操作をブロックして、コマンドが終了するまで待機しなければ操作を続行できません。 これに対して、長時間にわたる操作をバックグラウンド スレッドに割り当てることができれば、フォアグラウンド スレッドがアクティブなまま操作を続行できます。 たとえば、Windows アプリケーションでは、操作を実行中のユーザー インターフェイス スレッドの応答性を維持しながら、時間のかかる操作をバックグラウンド スレッドに委任することができます。
非同期操作
だそうです。
いやわかれ!
using System; using System.Net; using System.Threading; using System.IO; namespace Examples.System.Net { //他スレッドに渡す引数になるクラスです。 //その意味ではデータを保持するだけのクラス、と形容してもあながち間違いではないでしょう。 public class FtpState { private ManualResetEvent wait; private FtpWebRequest request; private string fileName; private Exception operationException = null; string status; public FtpState() { wait = new ManualResetEvent(false); } public ManualResetEvent OperationComplete { get {return wait;} } public FtpWebRequest Request { get {return request;} set {request = value;} } public string FileName { get {return fileName;} set {fileName = value;} } public Exception OperationException { get {return operationException;} set {operationException = value;} } public string StatusDescription { get {return status;} set {status = value;} } } //Main関数が入ってる主要クラスです。 //今回の解析の本命?となりまする。 public class AsynchronousFtpUpLoader { // Command line arguments are two strings: // 1. The url that is the name of the file being uploaded to the server. // 2. The name of the file on the local machine. // public static void Main(string[] args) { // Create a Uri instance with the specified URI string. // If the URI is not correctly formed, the Uri constructor // will throw an exception. ManualResetEvent waitObject; //最初のハマりポイント(私的に) //Uriはftp://フォルダ名/ファイル名という形式にしましょうね。 Uri target = new Uri (args[0]); //ローカルに保存されてるファイル。 string fileName = args[1]; //バックグラウンドスレッドに渡す引数のインスタンス FtpState state = new FtpState(); //FtpWebRequestクラスのインスタンスを作ってるようです。FtpWebRequestはWebRequestクラスを継承してるらしく、WebRequestクラスのCreateメソッドを呼び出してます。その上でキャスティングしてます。 FtpWebRequest request = (FtpWebRequest)WebRequest.Create(target); //WebRequestのメソッド名ですね。 request.Method = WebRequestMethods.Ftp.UploadFile; // This example uses anonymous logon. // The request is anonymous by default; the credential does not have to be specified. // The example specifies the credential only to // control how actions are logged on the server. //Credentials:認証 すなわちそういうことだ。 request.Credentials = new NetworkCredential ("anonymous","janeDoe@contoso.com"); // Store the request in the object that we pass into the // asynchronous operations. //パラメータを設定してますね。 state.Request = request; state.FileName = fileName; // Get the event to wait on. //あぁなるほど。2つのスレッドで同じインスタンスを持つというわけですか。 waitObject = state.OperationComplete; // Asynchronously get the stream for the file contents. //バックグラウンドスレッドが動き出しました。すぐ戻ってきます。 //ちなみにC#ですと非同期メソッドの命名は[Begin」から始まるようですよ! request.BeginGetRequestStream( new AsyncCallback (EndGetStreamCallback), state ); // Block the current thread until all operations are complete. //さきほどの待機オブジェクト。ここで終了を待ってるようです。 waitObject.WaitOne(); //非同期処理が終了しました。 // The operations either completed or threw an exception. if (state.OperationException != null) { throw state.OperationException; } else { Console.WriteLine("The operation completed - {0}", state.StatusDescription); } } //非同期処理メソッドです。なんでそんなこと分かるかと言えば //new AsyncCallback (EndGetStreamCallback)でこの関数を指定してるからなんです。 //あぁなつかしい。Win32API勉強してる頃、これがわからなかったのだ。 private static void EndGetStreamCallback(IAsyncResult ar) { //ちなみにこの関数はFTPファイル転送開始の時点で呼ばれます。 //非同期処理に投げた引数を受け取ります。 FtpState state = (FtpState) ar.AsyncState; Stream requestStream = null; // End the asynchronous call to get the request stream. try { //GetRequestを投げ終わったという意味ですね。 requestStream = state.Request.EndGetRequestStream(ar); // Copy the file contents to the request stream. //ローカル上のファイルを読み込み→リモートに書き込みますです。 const int bufferLength = 2048; byte[] buffer = new byte[bufferLength]; int count = 0; int readBytes = 0; FileStream stream = File.OpenRead(state.FileName); do { //ローカルから読み readBytes = stream.Read(buffer, 0, bufferLength); //リモートに書き込みます。 requestStream.Write(buffer, 0, readBytes); count += readBytes; } while (readBytes != 0); Console.WriteLine ("Writing {0} bytes to the stream.", count); // IMPORTANT: Close the request stream before sending the request. requestStream.Close(); // Asynchronously get the response to the upload request. state.Request.BeginGetResponse( new AsyncCallback (EndGetResponseCallback), state ); } // Return exceptions to the main application thread. catch (Exception e) { Console.WriteLine("Could not get the request stream."); state.OperationException = e; state.OperationComplete.Set(); return; } } // The EndGetResponseCallback method // completes a call to BeginGetResponse. private static void EndGetResponseCallback(IAsyncResult ar) { //FTP転送が終わりましたです。 //引数 FtpState state = (FtpState) ar.AsyncState; FtpWebResponse response = null; try { //FTP終わってるけど終了待ち response = (FtpWebResponse) state.Request.EndGetResponse(ar); response.Close(); state.StatusDescription = response.StatusDescription; // Signal the main application thread that // the operation is complete. //終了イベントをセットします。メインスレッドは終了したのを知ります。 //メインスレッドの待ちが終わります。 state.OperationComplete.Set(); } // Return exceptions to the main application thread. catch (Exception e) { Console.WriteLine ("Error getting response."); state.OperationException = e; state.OperationComplete.Set(); } } } }