Androidスマホおよび Androidタブレット間でファイル同期を図りたい。
記事[1]をベースにした次のプログラムをテストした。
部品化としては、接続(connect)、ディレクトリ移動、ファイルリスト入手、download、 upload、ファイル削除などが考えられる。 ディレクトリの生成や削除は後回しでいいだろう。
ミラーリングアップロード/ダウンロードは定期的(1日または1週間隔)またはメニュー操作で任意時間に実行する。
public void download(String sHost, int nPort, String sUser, String sPass, String sRemoteDir) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
FileOutputStream outputstream;
int nTimeout = 60 * 1000; //60秒
FTPClient ftpCli = null;
try {
ftpCli = new FTPClient();
//タイムアウト設定
ftpCli.setDefaultTimeout(nTimeout);
ftpCli.setConnectTimeout(nTimeout); //タイムアウトをキャッチするにはこれが必要
//接続
ftpCli.connect(sHost, nPort);
if (!FTPReply.isPositiveCompletion(ftpCli.getReplyCode())) {
throw new Exception("FTP接続エラー Code=" + ftpCli.getReplyCode());
}
//ソケットタイムアウト設定
ftpCli.setSoTimeout(nTimeout);
//ログイン
if (!ftpCli.login(sUser, sPass)) {
throw new Exception("FTP認証エラー Code=" + ftpCli.getReplyCode());
}
//ファイル転送モード設定
//ftpCli.setFileType(FTP.ASCII_FILE_TYPE); //テキストファイルなど
ftpCli.setFileType(FTP.BINARY_FILE_TYPE); //画像ファイルなど
//モード設定
ftpCli.enterLocalPassiveMode(); //パッシブモード
//ftpCli.enterLocalActiveMode(); //アクティブモード
//ディレクトリー移動
if (!ftpCli.changeWorkingDirectory(sRemoteDir)) {
throw new Exception("ディレクトリー移動エラー " + sRemoteDir + " Code=" + ftpCli.getReplyCode());
}
//受信
outputstream = new FileOutputStream("/storage/emulated/0/Map/download.txt", false);
ftpCli.retrieveFile("words_utf16.txt", outputstream);
outputstream.close();
} catch (NumberFormatException e) {
toastMake("Port異常");
} catch (FileNotFoundException e) {
//送信ファイルが無い時に発生
toastMake("送信ファイルがありません");
} catch (SocketException e) { // ソケット例外
//ポートの指定が無かった時などに発生
toastMake("中断されました " + e.toString());
} catch (SocketTimeoutException e) {
//接続先が違うなどの場合発生
toastMake("タイムアウトが発生しました");
} catch (Exception e) {
toastMake("エラー " + e.toString());
} finally {
if (ftpCli.isConnected()) {
try {
ftpCli.disconnect();
} catch (IOException e) {
// ignore
}
}
}
}
private void toastMake(String message) {
Toast toast = Toast.makeText(this, message, Toast.LENGTH_LONG);
toast.show();
}
まだ、ゴールに達していないが、少し、部品化を行った。
enum Command { mirroring_download, mirroring_upload }
public class FTPClientThread extends Thread {
ConstraintLayout layout;
Command cmd; // mirroring_download, mirroring_upload
String dirRemote;
String dirLocal;
public FTPClientThread(ConstraintLayout layout, Command cmd, String remote, String local) {
this.layout = layout;
this.cmd = cmd;
this.dirRemote = remote;
this.dirLocal = local;
}
@Override
public void run() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
long start = System.currentTimeMillis();
FTPClient ftpCli = connectFTP("サーバ", 21, "ユーザID", "パスワード");
if (ftpCli == null) {
return;
}
try {
if (!ftpCli.changeWorkingDirectory(dirRemote)) {
throw new Exception("ディレクトリー移動エラー " + dirRemote +
" Code=" + ftpCli.getReplyCode());
}
FTPFile[] ftpFiles = ftpCli.listFiles();
for (FTPFile file : ftpFiles) {
String name = file.getName();
if (name.startsWith(".")) continue;
File fileObj = new File(Map.DIR + dirLocal + "/" + name);
Files.createFile(fileObj.toPath());
try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(fileObj))) {
// ftpclient.retrieveFile will get the file from Ftp server and write it in outputStream.
boolean isFileRetrieve = ftpCli.retrieveFile(file.getName(), outputStream);
}
}
disconnectFTP(ftpCli);
} catch (Exception e) {
snackbarMake(e.toString());
}
long elapsed = System.currentTimeMillis() - start;
snackbarMake((elapsed/1000) + "秒");
/*
Handler mainThreadHandler = new Handler(Looper.getMainLooper());
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
//メインスレッドで実行する処理
snackbarMake((elapsed/1000) + "秒");
}
});
*/
}
FTPClient connectFTP(String sHost, int nPort, String sUser, String sPass) {
FTPClient ftpCli = null;
try {
ftpCli = new FTPClient();
//タイムアウト設定
int nTimeout = 60 * 1000; //60秒
ftpCli.setDefaultTimeout(nTimeout);
ftpCli.setConnectTimeout(nTimeout); //タイムアウトをキャッチするにはこれが必要
//接続
ftpCli.connect(sHost, nPort);
if (!FTPReply.isPositiveCompletion(ftpCli.getReplyCode())) {
throw new Exception("FTP接続エラー Code=" + ftpCli.getReplyCode());
}
//ソケットタイムアウト設定
ftpCli.setSoTimeout(nTimeout);
//ログイン
if (!ftpCli.login(sUser, sPass)) {
throw new Exception("FTP認証エラー Code=" + ftpCli.getReplyCode());
}
//ファイル転送モード設定
//ftpCli.setFileType(FTP.ASCII_FILE_TYPE); //テキストファイルなど
ftpCli.setFileType(FTP.BINARY_FILE_TYPE); //画像ファイルなど
//モード設定
ftpCli.enterLocalPassiveMode(); //パッシブモード
//ftpCli.enterLocalActiveMode(); //アクティブモード
} catch (Exception e) {
snackbarMake(e.toString());
ftpCli = null;
}
return ftpCli;
}
void disconnectFTP(FTPClient ftpCli) {
if (ftpCli != null && ftpCli.isConnected()) {
try {
ftpCli.disconnect();
} catch (IOException e) {
// ignore
}
}
}
private void snackbarMake(String message) {
Snackbar.make(layout, message, 10000).show();
}
}
次のプログラムでテストした。サブディレクトリには対応していないが、ファイルはダウンロードできた。 また、まだミラーリング処理は行っていない。
ConstraintLayout layout = findViewById(R.id.constraintLayout);
FTPClientThread thread = new FTPClientThread(layout, Command.mirroring_download, "html_css", "html_css");
thread.start();