Symfoware

Symfowareについての考察blog

Java + MockFtpServerでFTPサーバー側のプログラムを作成する その1

画像データを、データベースにバイナリデータとして登録しているとします。
画像をファイルとして取り出すには、何かしらのツールを作ることになるかと。

1つのファイルだけならwebアプリで簡単に実装できそうですが、
複数ファイルとなるとちょっと面倒です。

ふと、仮想的にFTPサーバーを公開して、ダウンロードさせればいいんじゃないか?と
思ったので試してみます。
実現できたら、任意のFTPクライアントで手軽にダウンロードできますね。


使用するライブラリ



軽く調べてみると、FTPサーバーのJava実装はこんなのがあるようです。

Apache FtpServer

MockFtpServer


ライブラリの更新日付を見てみると、MockFtpServerの方が
活発に開発されているようですので、こちらを使用することにしました。




開発環境の準備




こちらからMockFtpServer-2.5-bin.tar.gzをダウンロードしました。
MockFtpServer

解答したフォルダの中にあるMockFtpServer-2.5.jarをビルドパスに含めます。


MockFtpServerは、slf4jに依存しています。
こちらからslf4j-1.7.10.tar.gzをダウンロード。
http://www.slf4j.org/download.html

slf4j-api-1.7.10.jar
slf4j-simple-1.7.10.jar

この2つのjarをビルドパスに含めます。
これがないと、起動時に以下のエラーが発生します。


Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
    at org.mockftpserver.core.server.AbstractFtpServer.<init>(AbstractFtpServer.java:81)
    at org.mockftpserver.stub.StubFtpServer.<init>(StubFtpServer.java:96)
    at sample.MainProccess.main(MainProccess.java:9)








とりあえず起動するサンプル



とりあえずftpサーバーとして起動するサンプルは以下のとおり。


  1. import org.mockftpserver.stub.StubFtpServer;
  2. public class MainProccess {
  3.     
  4.     public static void main(String... args) {
  5.         
  6.         StubFtpServer stubFtpServer = new StubFtpServer();
  7.         stubFtpServer.setServerControlPort(10021);
  8.         stubFtpServer.start();
  9.         
  10.     }
  11. }





ポート番号をデフォルトの21ではなく10021に変更しています。
開発をユーザー権限で行っている都合上、21ポートで起動しようとすると、こんなエラーが発生します。


[Thread-0] INFO org.mockftpserver.stub.StubFtpServer - Starting the server on port 21
[Thread-0] ERROR org.mockftpserver.stub.StubFtpServer - Error
java.net.BindException: 許可がありません
    at java.net.PlainSocketImpl.socketBind(Native Method)
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:376)
    at java.net.ServerSocket.bind(ServerSocket.java:376)
    at java.net.ServerSocket.<init>(ServerSocket.java:237)
    at java.net.ServerSocket.<init>(ServerSocket.java:128)
    at org.mockftpserver.core.socket.DefaultServerSocketFactory.createServerSocket(DefaultServerSocketFactory.java:39)
    at org.mockftpserver.core.server.AbstractFtpServer.run(AbstractFtpServer.java:140)
    at java.lang.Thread.run(Thread.java:745)
[Thread-0] INFO org.mockftpserver.stub.StubFtpServer - Server stopped.





実行すると、ftpサーバーが起動し、10021ポートで待ち受けます。
ftpコマンドで接続を試してみましよう。

ftpコマンド起動後、「open localhost 10021」で接続します。
ユーザー名、パスワードは空白のままエンターでOKです。


$ ftp
ftp> open localhost 10021
Connected to localhost.
220 Service ready for new user. (MockFtpServer 2.5; see http://mockftpserver.sourceforge.net)
Name (localhost:baranche):
331 User name okay, need password.
Password:
501 Syntax error in parameters or arguments.
Login failed.
Remote system type is "WINDOWS".
ftp>





なんか表示されました。
サーバー側のログも出力されています。


[Thread-0] INFO org.mockftpserver.stub.StubFtpServer - Starting the server on port 10021
[Thread-0] INFO org.mockftpserver.stub.StubFtpServer - Connection accepted from host /127.0.0.1
[Thread-1] INFO org.mockftpserver.core.command.ConnectCommandHandler - Sending reply [220 Service ready for new user. (MockFtpServer 2.5; see http://mockftpserver.sourceforge.net)]
[Thread-1] INFO org.mockftpserver.core.session.DefaultSession - Received command: [USER baranche]
[Thread-1] INFO org.mockftpserver.stub.command.UserCommandHandler - Sending reply [331 User name okay, need password.]
[Thread-1] INFO org.mockftpserver.core.session.DefaultSession - Received command: [PASS ]
[Thread-1] INFO org.mockftpserver.stub.command.PassCommandHandler - Sending reply [501 Syntax error in parameters or arguments.]
[Thread-1] INFO org.mockftpserver.core.session.DefaultSession - Received command: [SYST]
[Thread-1] INFO org.mockftpserver.stub.command.SystCommandHandler - Sending reply [215 "WINDOWS" system type.]




ftpサーバーの起動まではできた模様。





ユーザー認証の実装



ユーザー認証部分を作ってみます。

FTP Commands and CommandHandlers

こちらを参考に、

・ユーザー名の入力は「org.mockftpserver.stub.command.UserCommandHandler」
・パスワードの入力は「org.mockftpserver.stub.command.PassCommandHandler」

をそれぞれ継承して実装すれば良さそうです。

実装方法は

org.mockftpserver.fake.command.UserCommandHandler
org.mockftpserver.fake.command.PassCommandHandler

が参考になりました。


見様見真似で作ったサンプルはこちら。

・MyUserCommandHandler


  1. import org.mockftpserver.core.command.Command;
  2. import org.mockftpserver.core.command.InvocationRecord;
  3. import org.mockftpserver.core.command.ReplyCodes;
  4. import org.mockftpserver.core.session.Session;
  5. import org.mockftpserver.core.session.SessionKeys;
  6. import org.mockftpserver.stub.command.UserCommandHandler;
  7. public class MyUserCommandHandler extends UserCommandHandler {
  8.     
  9.     public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
  10.         
  11.         // 入力されたユーザー名を取得し、セッションに退避
  12.         String username = command.getRequiredParameter(0);
  13.         session.setAttribute(SessionKeys.USERNAME, username);
  14.         
  15.         if (replyCode == 0) {
  16.             sendReply(session, ReplyCodes.USER_NEED_PASSWORD_OK, replyMessageKey, replyText, null);
  17.         } else {
  18.             sendReply(session);
  19.         }
  20.     }
  21. }





・MyPassCommandHandler


  1. import org.mockftpserver.core.command.Command;
  2. import org.mockftpserver.core.command.InvocationRecord;
  3. import org.mockftpserver.core.command.ReplyCodes;
  4. import org.mockftpserver.core.session.Session;
  5. import org.mockftpserver.core.session.SessionKeys;
  6. import org.mockftpserver.stub.command.PassCommandHandler;
  7. public class MyPassCommandHandler extends PassCommandHandler {
  8.     
  9.     public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) {
  10.         
  11.         // 入力されたパスワードを退避
  12.         String password = command.getRequiredParameter(0);
  13.         // セッションからユーザー名を取得
  14.         String username = (String)session.getAttribute(SessionKeys.USERNAME);
  15.         
  16.         // ユーザー名、パスワードをチェック
  17.         if ("symfo".equals(username) && "pass".equals(password)) {
  18.             replyCode = ReplyCodes.PASS_OK;
  19.             sendReply(session);
  20.             return;
  21.         }
  22.         
  23.         // ログイン失敗
  24.         replyCode = ReplyCodes.PASS_LOG_IN_FAILED;
  25.         sendReply(session);
  26.         
  27.     }
  28. }





作成したhandlerをサーバーに登録します。


  1. import org.mockftpserver.core.command.CommandNames;
  2. import org.mockftpserver.stub.StubFtpServer;
  3. public class MainProccess {
  4.     
  5.     public static void main(String... args) {
  6.         
  7.         StubFtpServer stubFtpServer = new StubFtpServer();
  8.         stubFtpServer.setServerControlPort(10021);
  9.         
  10.         stubFtpServer.setCommandHandler(CommandNames.USER, new MyUserCommandHandler());
  11.         stubFtpServer.setCommandHandler(CommandNames.PASS, new MyPassCommandHandler());
  12.         
  13.         stubFtpServer.start();
  14.         
  15.     }
  16. }




これでユーザー名、パスワードを間違えた時エラーとなりました。
先は長いです。
関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2015/01/11(日) 17:55:52|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<Java + MockFtpServerでFTPサーバー側のプログラムを作成する その2 | ホーム | PHPで画像リサイズの高速化(setOption,libjpeg-turbo使用)>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1580-229bc523
この記事にトラックバックする(FC2ブログユーザー)