2019 Jul. 15.
MyJsch.java
package your.package;
import android.content.Context;
import com.jcraft.jsch.Channel;
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;
import com.jcraft.jsch.UserInfo;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
class MyJsch {
private final String ServerIp;
private final int Port;
private final String User;
private final String PassPhraseWord;
private final String IdentityKeyPath;
private final Context AppContext;
/** Channel接続タイプ */
private static final String CHANNEL_TYPE = "sftp";
/*
* コンストラクタ
*/
MyJsch( final String ServerIp, final int Port, final String User, final String PassPhraseWord,
final String IdentityKeyPath, final Context AppContext) {
this.ServerIp = ServerIp;
this.Port = Port;
this.User = User;
this.PassPhraseWord = PassPhraseWord;
this.IdentityKeyPath = IdentityKeyPath;
this.AppContext = AppContext;
}
/*
* Sessionを開始
*/
protected Session connectSession() throws JSchException {
Session session = null;
// android端末内の外部ストーリッジ確認
List sdCardFilesDirPaths =
SdCardDirPaths.getSdCardFilesDirPathListForLollipop( AppContext );
Collections.sort(sdCardFilesDirPaths, new CompStringLength());
String externalPath = sdCardFilesDirPaths.get(0);
externalPath = externalPath.replaceAll("/Android.*$", "");
final JSch jsch = new JSch();
// クライアント側のknownHostsチェックを行わない
Hashtable config = new Hashtable();
config.put("StrictHostKeyChecking", "no");
jsch.setConfig(config);
// パスフレーズ・秘密鍵方式
String privKeyFilePath = externalPath + "/" + IdentityKeyPath;
File privKeyFile = new File(privKeyFilePath);
if (privKeyFile.exists() ) {
jsch.addIdentity(privKeyFilePath, PassPhraseWord);
}
// Session取得
session = jsch.getSession(User, ServerIp, Port);
// パスワード方式でも可とする
session.setPassword(PassPhraseWord);
final UserInfo userInfo = new SftpUserInfo();
session.setUserInfo(userInfo);
session.connect();
return session;
}
/**
* SFTPのChannelを開始
*
* @param session
* 開始されたSession情報
*/
protected ChannelSftp connectChannelSftp(final Session session)
throws JSchException {
final ChannelSftp channel = (ChannelSftp) session.openChannel(CHANNEL_TYPE);
try {
channel.connect();
} catch (JSchException e) {
return null;
}
return channel;
}
/**
* Session・Channelの終了
*
* @param session
* 開始されたSession情報
* @param channels
* 開始されたChannel情報.複数指定可能
*/
protected void disconnect(final Session session, final Channel... channels) {
if (channels != null) {
for (Channel c: channels ) {
if (c != null) {
c.disconnect();
}
}
}
if (session != null) {
session.disconnect();
}
}
/**
* SFTPに接続するユーザ情報を保持するクラス
*/
private static class SftpUserInfo implements UserInfo {
@Override
public String getPassword() {
return null;
}
@Override
public boolean promptPassword(String arg0) {
return true;
}
@Override
public boolean promptPassphrase(String arg0) {
return true;
}
@Override
public boolean promptYesNo(String arg0) {
return true;
}
@Override
public void showMessage(String arg0) {
}
@Override
public String getPassphrase() {
return null;
}
}
/**
* ファイルアップロード
*
* @throws JSchException
* Session・Channelの設定/接続エラー時に発生
*/
public void putFile(ChannelSftp channel, InputStream inStream, String destPath)
throws SftpException {
/*
* sftp通信を実行
*/
String absoluteDestPath = channel.getHome() + "/" + destPath;
String destFile = new File(absoluteDestPath).getName();
int numDestPath = absoluteDestPath.length();
int numDestFile = destFile.length();
String destParentPath = absoluteDestPath.substring( 0, numDestPath - numDestFile );
channel.cd(destParentPath);
channel.put( inStream, destFile);
// confirm existance of destFile
channel.lstat(destFile);
}
/*
* サーバー上のファイルの削除
* destFileNameFront文字列から始まるファイルを全て削除する
*/
public void deleteFiles(ChannelSftp channel, String destFileNameFront) throws SftpException {
channel.rm(destFileNameFront + "*");
}
}
SdCardDirPaths.java
package your.package;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class SdCardDirPaths {
/**
* SDカードのfilesディレクトリパスのリストを取得する。
* Android5.0以上対応。
*
* @param context
* @return SDカードのfilesディレクトリパスのリスト
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static List getSdCardFilesDirPathListForLollipop(Context context) {
List sdCardFilesDirPathList = new ArrayList<>();
// getExternalFilesDirsはAndroid4.4から利用できるAPI。
// filesディレクトリのリストを取得できる。
File[] dirArr = context.getExternalFilesDirs(null);
for (File dir : dirArr) {
if (dir != null) {
String path = dir.getAbsolutePath();
// isExternalStorageRemovableはAndroid5.0から利用できるAPI。
// 取り外し可能かどうか(SDカードかどうか)を判定している。
if (Environment.isExternalStorageRemovable(dir)) {
// 取り外し可能であればSDカード。
// このパスをパスリストに加える
if (!sdCardFilesDirPathList.contains(path)) {
sdCardFilesDirPathList.add(path);
}
}
}
}
return sdCardFilesDirPathList;
}
}
(利用例)
Send2ServerTask.java
package your.package;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.Toast;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
// 複数のファイルをSSH送信する
public class Send2ServerTask extends AsyncTask {
final String ServerIp = "SSH.SERVER.IP.ADDRESS";
final int Port = 22;
final String User = "SshLogInUser";
final String IdentityKeyPath = "PRIVATE/KEY/FILE/PATH/IN/CLIENT/DEVICE";
// if IdentityKeyPath is illegal, password authentication is used.
final String DestParentPath = "SERVER/DIR/TO/BE/STORED";
private WeakReference weakRef;
private HashMap inStreamMap;
final private String passPhrase;
final private String fileNameFront;
// SSH失敗時のファイル削除のためにファイル名共通文字列を保持
// 複数の送信ファイルはいずれもfileNameFront文字列で始まるファイル名とすること
private Listener listener;
/*
* constructor
* inStreamMap 送信する複数ファイルのファイル名とインプットストリームをHashMapで渡す
*/
Send2ServerTask(Activity parentActivity, HashMap inStreamMap,
String passPhrase, String fileNameFront) {
weakRef = new WeakReference<>(parentActivity);
this.inStreamMap = new HashMap<>(inStreamMap);
this.passPhrase = passPhrase;
this.fileNameFront = fileNameFront;
}
@Override
protected String doInBackground(Void... params) {
MyJsch mJsch;
Session sftpSession = null;
ChannelSftp sftpChannel = null;
Activity refActivity = weakRef.get();
if (refActivity == null || refActivity.isFinishing()) {
return null;
}
Context appContext = weakRef.get().getApplicationContext();
// initialise Jsch
mJsch = new MyJsch(ServerIp, Port, User, passPhrase, IdentityKeyPath, appContext);
// connect session, channel
try {
sftpSession = mJsch.connectSession();
sftpChannel = mJsch.connectChannelSftp( sftpSession );
// upload
int count = 0;
for ( Map.Entry entry : inStreamMap.entrySet()) {
// entry.getKey() entry.getValue()
String destPath = DestParentPath + "/" + entry.getKey();
try {
mJsch.putFile(sftpChannel, entry.getValue(), destPath);
} catch (SftpException e) {
try {
// このセッションでアップロードされたファイルをすべて削除する
mJsch.deleteFiles(sftpChannel, fileNameFront);
mJsch.disconnect( sftpSession, sftpChannel);
return "Failed to upload files. count: " + count + ". " + destPath + " " + e.toString();
} catch (SftpException e1) {
mJsch.disconnect( sftpSession, sftpChannel);
return "Failed to delete remote files. " + e1.toString();
}
}
try {
// take sleep between SFTP#puts
Thread.sleep(500);
} catch (InterruptedException e) {
// このセッションでアップロードされたファイルをすべて削除する
mJsch.deleteFiles(sftpChannel, fileNameFront);
mJsch.disconnect( sftpSession, sftpChannel);
return "Failed! sleep" + e.toString();
}
count++;
}
if ( count != inStreamMap.size()) {
// このセッションでアップロードされたファイルをすべて削除する
mJsch.deleteFiles(sftpChannel, fileNameFront);
mJsch.disconnect( sftpSession, sftpChannel);
return "Could not upload " + inStreamMap.size() + " files";
}
} catch (JSchException | SftpException e) {
mJsch.disconnect( sftpSession, sftpChannel);
return "connection failed. " + e.toString();
} finally {
// disconnect channel, session
mJsch.disconnect( sftpSession, sftpChannel);
}
return "Succeeded. " + inStreamMap.size() + " files upload";
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Activity refActivity = weakRef.get();
if (refActivity == null || listener == null) {
Toast.makeText(refActivity, "failed sending to server.", Toast.LENGTH_LONG).show();
} else {
if ( result.contains("Succeeded") ) {
Toast.makeText(refActivity, result, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(refActivity, result, Toast.LENGTH_LONG).show();
}
listener.onSuccess(result);
}
return;
}
void setListener(Listener listener) {
this.listener = listener;
}
interface Listener {
void onSuccess(String str);
}
}
MyActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_confirm_before_send);
buttonOK = findViewById(R.id.buttonOK);
setListeners();
}
/*
* Send2ServerTaskへのリスナ
* サーバーへのアップロード終了後に行う処理を記述
*/
private Send2ServerTask.Listener createListener() {
return new Send2ServerTask.Listener() {
@Override
public void onSuccess(String result) {
deleteFiles();
}
};
}
protected void setListeners(){
buttonOK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* HashMap SSHサーバーに送るデータ
* String 送信先ファイルパス
* InputStream 送信ファイルのインプットストリーム
*/
HashMap dataMap = new HashMap<>();
setData2dataMap(); // dataMapにSSH通信データをセットする
setPassPhraseWord();
setFileNameFront();
/*
* dataMapを保管サーバーに送る
*/
send2serverTask = new Send2ServerTask(thisActivity, dataMap, passPhraseWord, fileNameFront);
// Listenerを設定し、send2serverTaskを実行してdataMapを保管サーバーに送る
send2serverTask.setListener(createListener());
send2serverTask.execute();
}
});
}