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();