sFTPによるファイル転送処理サンプル
JavaによるFTPのファイル転送を作ることになったのですが、セキュリティを考慮し、sFTPで通信経路を暗号化することになりました。
JavaによるsFTP通信に利用したのは、JCraftの「JSch – Java Secure Channel」というライブラリです。
また、今回は、ユーザー名とパスワードによる認証ではなく、privatekeyを読み込んでそれを利用して接続を行うという条件もありましたので、すこし手間取りました。
接続のサンプルはいくつか見つけたのですが、リモートサーバ側のシェルを実行するサンプルはあまり無かったので、備忘録的に上げておきたいと思います。
なお、ローカルの接続テストのために、フリーのSFTPサーバーを立てたのですが、「freeSSHd」を利用させていただきました。
リモートサーバのバッチファイルを実行するには、接続するユーザの「Shell」実行権限が付いていないといけないので、注意してください。
以下は、実際のJavaサンプルです。
UserInfoの実装クラス
package jp.koji.jsch; import com.jcraft.jsch.UserInfo; public class MyUserInfo implements UserInfo { public String getPassword() { return null; } //PASSFRASEは空 public String getPassphrase() { return ""; } public boolean promptPassword(String arg0) { return true; } public boolean promptPassphrase(String arg0) { return true; } public boolean promptYesNo(String arg0) { return true; } public void showMessage(String arg0) { } }
SampleTestクラス
package jp.koji.batch; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; import jp.koji.jsch.MyUserInfo; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import com.jcraft.jsch.SftpException; public class JschTest { private static final String hostname = "XXXXXX"; private static final String userid = "XXXXXXX"; public static void main(String[] arg) throws JSchException, SftpException ,Exception{ JSch jsch = new JSch(); jsch.addIdentity("C:\PRIVATEKEY\keyname_pk"); Session session = jsch.getSession(userid, hostname, 22); session.setUserInfo(new MyUserInfo()); session.connect(); //sftp MODEでの接続 ChannelSftp channel = (ChannelSftp) session.openChannel("sftp"); channel.connect(); List list = channel.ls("."); System.out.println("---- ls"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } channel.disconnect(); //COMMAND実行 MODEでの接続 ChannelExec channel2=(ChannelExec)session.openChannel("exec"); channel2.setCommand("C:XXXXXXXXXXXSAMPLE.bat"); channel2.connect(); InputStreamReader inputStreamReader = null; inputStreamReader = new InputStreamReader( channel2.getInputStream(), "SJIS" ); BufferedReader bufferedReader = null; bufferedReader = new BufferedReader( inputStreamReader ); StringBuffer sblog = new StringBuffer(""); while( true ) { // 1文字取得 int i = bufferedReader.read(); if( i == -1 ) { //-1で終了 break; } // 出力 sblog.append( (char)i ); } System.out.println(sblog.toString()); if (channel2.isClosed()) { System.out.println("status: "+channel2.getExitStatus()); } channel2.disconnect(); session.disconnect(); } }
始めまして。通りがかりのものです。
とても参考になりました。ありがとうございます。
お陰様で、私も実装できたのですが、JSchですと
DLがあまりにも遅く使い物になりません。。。
ESエクスプローラーで2,3秒のものが、JSchだと20分近くかかります。
何か良い手は無いものでしょうか?
ESエクスプローラーは何をつかっているのでしょう??
「ESエクスプローラー」というのをはじめて聞きました。調べてみるとAndroidのアプリのようでしたが、私の認識あってますでしょうか。
あっている前提で、Androidのアプリか何かで上記のサンプルを使われたと推測しますが。。。
私が上記を使ったのは、PC上のバッチアプリですので、スマホでのパフォーマンスだとしたら、あまり高速化の方策は詳しくないですね。すみません。。
それにしても、ESエクスプローラーで2、3秒のものがJSchだと20分とは、ファイルの容量が大きいのでしょうか?
返信ありがとうございます。
また説明不足で失礼しました。
ESエクスプローラーはAndroidアプリになります。
現在、AndroidアプリにてSFTP(DL)を実現しようと試みております。
JSchを使えば実現することは可能なのですが、Androidですと、
速度の問題がどうしても解決できません。
BufferedInputStream/BufferedOutputStream を使って、
whileでぐるぐる回してみると、JSchが一度にGetできるのは
32Kでした。
22Mのファイルですと、1分半ぐらいかかります。
ESエクスプローラーですと、ものの数秒です。
確かにJSchのソースをみると、32kの縛りがあるのですが、
そこ(LOCAL_MAXIMUM_PACKET_SIZE)を大きいサイズ
にしても32kのままでした。
(修正したところが悪いのかもしれませんが・・・)
悩ましいです・・・。
前回投稿の訂正:
>ESエクスプローラーで2、3秒のものがJSchだと20分
正しくは
20分かかるファイルは、ESエクスプローラーでは数十秒でした。