2010年12月20日

JavaでSFTP

前回のSSHに続きOrion SSH2を利用してJavaでSFTPします。具体的にはSFTPでリモートホストへのPUTを例に説明します。

まず注意点ですが、Orion SSH2にはGanymed -> Trileadという歴史がありますが、Trilead SSH-2 build211以前にはバグがあり、正常に動作しません。

-- Trilead SSH-2 for Java build211, 2007-10-28: --
Fixed the write() method in the SFTP code.
Unsatisfiable preconditions stopped the method from
writing any bytes. As stated in the documentation,
the SFTP code is still experimental.
Thanks to Andreas Pueschel.

Orion SSH2を使う限り反映済みですが、Orion以外のライブラリを使っている場合は注意下さい。

では、早速コードから。 

    String host = "localhost";
    String user = "hoge";
    String password = "password";
    
    Connection conn = new Connection(host, 22);
    
    try {
      /*
       * Paramerters:
       *   ServerHostKeyVerifier, 
       *   connectTimeoutInMillis (zero means no timeout), 
       *   kexTimeoutInMillis (zero means no timeout)
       */
      conn.connect(null, 10000, 0);
      
      // auth
      if ( conn.authenticateWithPassword(user, password) ) {
        SFTPv3Client sftp = new SFTPv3Client(conn);
        sftp.setCharset("ISO-8859-1");
        
        // put file
        String localFile = "sample.txt";
        String remoteDir = "/tmp";
        String remotePath = remoteDir + "/" + localFile;
        
        SFTPv3FileHandle sftpFileHandle = null;
        BufferedInputStream filein = null;
        
        try {
          // create remote file
          sftpFileHandle = sftp.createFile(remotePath);
          
          // set permission.
          SFTPv3FileAttributes attr = new SFTPv3FileAttributes();
          /* 
           * POSIX permissions. NULL if not present.
           *   S_IRWXU    00700     mask for file owner permissions
           */
          attr.permissions = new Integer(00744); // rwxr--r--
          sftp.setstat(remotePath, attr);

          // load local file.
          filein = new BufferedInputStream(new FileInputStream(localFile));
          
          // write remote file.
          System.out.println("putting " + localFile +"...");
          byte[] buffer = new byte[1024];
          long offset = 0;
          int readLength = 0;
          while ( (readLength = filein.read(buffer)) != -1 ) {
            /*
             * write params.
             * handle - a SFTPv3FileHandle handle.
             * fileOffset - offset (in bytes) in the file.
             * src - the source byte array.
             * srcoff - offset in the source byte array.
             * len - how many bytes to write.
             */
            sftp.write(sftpFileHandle, offset, buffer, 0, readLength);
            offset += readLength;
          }
          // compare size.
          long remoteFileSize = sftp.stat(remotePath).size;
          if (remoteFileSize == new File(localFile).length()) {
            System.out.println("succeed: put " + localFile);
            // list remote files.
            Vector v = sftp.ls(remoteDir);
            for (int i=0; i < v.size(); i++) {
              SFTPv3DirectoryEntry entry = (SFTPv3DirectoryEntry) v.get(i);
              System.out.println(remoteDir + "> " + entry.filename);
            }
          } else {
            System.out.println("failed: file size is different from remote file. remote:" + 
                remoteFileSize+", local:"+localFile.length());
            sftp.rm(remotePath);
          }
          filein.close();
          sftp.closeFile(sftpFileHandle);
        } catch (IOException e) {
          e.printStackTrace();
        }
        
      } else {
        System.out.println("login failed.");
      }      
    } catch (IOException e) {
      e.printStackTrace();
    }

コネクションを作成し、認証するまでは前回のSSHでコマンド実行と同じです。
 
SFTPの操作はSFTPv3Clientクラス経由で行うので、このクラスのコンストラクタにコネクションを渡します。
 
putするファイルはsample.txtで、リモートホストの/tmpに送ります。

sftp.createFile(remotePath);でファイルの作成と合わせてSFTPv3FileHandleインスタンスを取得します。

SFTPv3FileAttributesでファイルのパーミッションを設定し、sftp.setstat(remotePath, attr);で反映しています。

ちなみに、attr.permissions = new Integer(-1); だとパーミッションは777になります。

いよいよ準備が出来たので、

while ( (readLength = filein.read(buffer)) != -1 ) {・・・}

ループでリモートホストにsftp.writeしています。

最後に、リモートホスト上のファイルとローカルのファイルのサイズを比較し、同じであればput完了として、リモートホスト上のファイル一覧を表示しています。

実行結果は次の通りです。
 
putting sample.txt...
succeed: put sample.txt
/tmp> ..
/tmp> sample.txt
/tmp> .font-unix

 一点、不満がありましてputもgetもですが、転送速度が400KB/s(3.2Mb/s)程度しか出ないんです。
#早くする方法があれば誰か教えて欲しい(え、十分ですか?)。
 
ちなみにgetする場合のコードは次の通りです。
 
        SFTPv3Client sftp = new SFTPv3Client(conn);
        
        long remoteFileSize = sftp.stat(remotePath).size;
        // open remote file with read only
        sftpFileHandle = sftp.openFileRO(remotePath);
        
        // get data
        System.out.println("getting " + remotePath + "...");
        bos = new BufferedOutputStream(new FileOutputStream(localFile));
        byte[] buffer = new byte[1024];
        long offset = 0;
        int readLength = 0;
        while( (readLength = sftp.read(sftpFileHandle, offset, buffer, 0, buffer.length)) != -1){
          /*
           * int ch.ethz.ssh2.SFTPv3Client.read(SFTPv3FileHandle handle,
           * long fileOffset, byte[] dst, int dstoff, int len) throws IOException
           *
           * Note:
           * This element neither has attached source nor attached
           * Javadoc and hence no Javadoc could be found.
           */
          bos.write(buffer, 0, readLength);
          offset += readLength;
        }
        // flush
        bos.flush();        
        // close
        bos.close();
        sftp.closeFile(sftpFileHandle);        
        
        // compare
        if (localFile.length() == remoteFileSize) {
          System.out.println("succeed: get " + remotePath);
        } else {
          System.out.println("failed: file size is different from remote. remote:" + 
              remoteFileSize+", local:"+localFile.length());
          localFile.delete();
        }
        

では。 

入門OpenSSH―Linux/FreeBSD/Solaris/Mac OS X対応
新山 祐介 
秀和システム 
売り上げランキング: 178430

Posted by netbuffalo at 19:08│Comments(0)TrackBack(0)Java | プログラミング


この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/netbuffalo/1939727

コメントする

名前
URL
 
  絵文字