Java ソースコード (FTPSample.java)
FTPによる送信を行うサンプル。FTPサーバーアドレス,ユーザ名,パスワード,送信するファイル名などはプログラム中に記述してください。
送受信、ファイル一覧、パッシブモード、CWDなどを実装したGUI版FTPクライアントサンプルはこちらのJava29.phpをご覧ください。NetBeansを使用したGUIプログラムで、自動生成部分を除いたソースコードを掲載しています。
//ボタンAをクリックするとファイル送信を行う簡単なサンプル。 import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; public class FTPSample { private static final int CTRLPORT = 21; // ftpの制御用のポート private static Socket ctrlSocket; // 制御用ソケット private static PrintWriter ctrlOutput; // 制御出力用ストリーム private static BufferedReader ctrlInput; // 制御入力用ストリーム private static byte[] localHostAddress; // ローカルホストのアドレス public static void main(String[] args) { //フレームを作成してレイアウトをセット JFrame myFrame = new JFrame("FTP File Send"); myFrame.getContentPane().setLayout(new BorderLayout()); //パネルを作成してレイアウトをセット JPanel myPanel = new JPanel(); myPanel.setLayout(new GridLayout(1, 3, 5, 5)); //表示用ラベルを作成 final JLabel myLabel = new JLabel("ボタンをクリックして下さい", JLabel.CENTER); //btnAを作成 JButton btnA = new JButton("btnA"); //btnAがクリックされた時のイベントを定義 btnA.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { myLabel.setText("btnAがクリックされました"); try { /** 設定してください **/ String host = "xxx.somewhere.org"; //FTPサーバー URL String loginName = "yamada"; //FTPユーザー名 String password = "ftpのpassword"; String dirName = "/"; String fileName = "/testdata.txt"; // 接続します ctrlSocket = new Socket(host, CTRLPORT); localHostAddress = ctrlSocket.getLocalAddress().getAddress(); ctrlOutput = new PrintWriter(ctrlSocket.getOutputStream()); ctrlInput = new BufferedReader(new InputStreamReader(ctrlSocket.getInputStream())); // ユーザー認証します ctrlOutput.println("USER " + loginName); ctrlOutput.flush(); ctrlOutput.println("PASS " + password); ctrlOutput.flush(); // 指定したディレクトリに移動します ctrlOutput.println("CWD " + dirName); ctrlOutput.flush(); // バイナリモードに設定します(アスキーモードの場合は'TYPE A') ctrlOutput.println("TYPE I"); ctrlOutput.flush(); // アップロードします FileInputStream fis = new FileInputStream(fileName); Socket dataSocket = dataConnection("STOR " + fileName); OutputStream outstr = dataSocket.getOutputStream(); int n; byte[] buff = new byte[1024]; while ((n = fis.read(buff)) > 0) { outstr.write(buff,0,n); } dataSocket.close(); fis.close(); // 接続を閉じます ctrlOutput.close(); ctrlInput.close(); ctrlSocket.close(); }catch (Exception e) { e.printStackTrace(); } } }); //パネルにボタンをセット myPanel.add(btnA); //フレームにパネルをセット(BorderLayoutの北側) myFrame.getContentPane().add(myPanel, BorderLayout.NORTH); //フレームにラベルをセット(BorderLayoutの残りエリア) myFrame.getContentPane().add(myLabel, BorderLayout.CENTER); //フレーム(ウィンドウ)が閉じる際の処理を定義 myFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); //Look & Feelの設定 try { //WindowsスタイルのLook&Feelに設定 UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); // 設定を反映させる SwingUtilities.updateComponentTreeUI(myFrame); //エラー処理ブロック } catch (Exception e) { } //フレームのサイズを設定して表示 myFrame.setSize(250, 100); myFrame.setVisible(true); } private static Socket dataConnection(String ctrlcmd) throws IOException,UnknownHostException { String cmd = "PORT "; ServerSocket serverDataSocket = new ServerSocket(0,1); for (int i=0;i<4;i++) { cmd = cmd + (localHostAddress[i] & 0xff) + ","; } cmd = cmd + (((serverDataSocket.getLocalPort())/256) & 0xff) + "," + (serverDataSocket.getLocalPort() & 0xff); ctrlOutput.println(cmd); ctrlOutput.flush(); ctrlOutput.println(ctrlcmd); ctrlOutput.flush(); Socket dataSocket = serverDataSocket.accept(); serverDataSocket.close(); return dataSocket; } }
画面例
下の画像は、実行時の画像です。
Kom., 2011
少しだけ解説を加えておきます。FTPサーバーは通常21番ポートでクライアントからの接続を待受けしています。
ctrlSocket = new Socket(host, CTRLPORT); localHostAddress = ctrlSocket.getLocalAddress().getAddress(); ctrlOutput = new PrintWriter(ctrlSocket.getOutputStream()); ctrlInput = new BufferedReader(new InputStreamReader(ctrlSocket.getInputStream()));
により、サーバーに接続してSocketを準備、それをもとにBufferedReaderのctrlInputまでを作成しておきます。
ctrlOutputとctrlInputはFTPサーバーとの命令やレスポンス文字列の送受信に使われますが、
ファイルデータ自体の送受信や、ファイルリストの送受信には使われません。これらの通信のためには別のSocketを用意します。
このソケットはServerSocketです。つまり、クライアントがサーバーのように待受けを行い、サーバーから接続してもらいます。そのためにサーバーにはこちらのIPアドレスと待受け用のポート番号を知らせてやります。
private static Socket dataConnection()メソッドで行っているのが、この処理です。
サーバーから接続を受けたら、その後の通信用ソケットをserverDataSocket.accept()が作成してdataSocketに入れ、ファイル送信に使用するようになっています。
このように2本の通信経路を用意して、コマンドなどの情報のやり取りと、データの送受信に使い分けているわけです。FTPではファイル転送の途中で中断などをしたい場合、一つのソケットのみで通信を行っていたとしたらファイル転送の終了まで中断コマンドすら送れなくなってしまいます。そこでコントロール用とデータ用のソケットを別々に用意していると考えればよいでしょう。
なお、上記プログラムで使用しているPORTコマンドではクライアントがサーバーのようにふるまってデータ用ソケットを作るわけですが、
ファイアウォールなどが設置してあると通常は外部からPCへ接続することは、そのポートを外部に対して開けておかない限りできません。
これでは困りますから、PASSIVEモードというものが用意されており、このモードではクライアントがデータ用Socketを用意したい場合は、サーバーからデータ接続用のポート番号を指定してもらい、そこに接続してデータ通信用経路を確保することになっています。
このサンプルではActiveモードのみであるのでその処理はやっておらず、したがって外部のFTPサーバーにはたいていの場合ファイアウォールで止められて接続できないでしょう。ただし、1台のPC上でサーバーとクライアントの両方を動かせばファイアウォールの影響を受けずに「自分」から「自分」へと通信を行えますから、動作確認は問題なく行うことができます。
FTPクライアントを実装するJavaプログラミングの参考ソースコードでは、PASSIVEモードで
送受信を行うようにしています。比較してみてください。