HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

Apache MINAでSFTP接続

はじめに

必要に迫られてFTPS接続するJavaプログラムをSFTP接続するように書き換える際に、Apache MINAを使って動く形に書き下されたサンプルが見つからなかったので、そのメモ。

要件としてパスワード認証を使うというのもあった。

事前準備

必要なライブラリをダウンロードする。

Apache MINA / Apache SSHD

今回のメインライブラリ。

今回は、「Apache MINA 2.2.3」「Apache SSHD 2.11.0」を使用した。

Apache Commons IO

I/O周りを扱う上では何かと便利。

今回は、「Commons IO 2.16.1」を使用した。

メインプログラム

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;

import org.apache.commons.io.IOUtils;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.sftp.client.SftpClient;
import org.apache.sshd.sftp.client.SftpClient.Attributes;
import org.apache.sshd.sftp.client.SftpClient.CloseableHandle;
import org.apache.sshd.sftp.client.SftpClient.DirEntry;
import org.apache.sshd.sftp.client.SftpClientFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class SftpFile {
    private final String filename;
    private final String longFilename;
    private final Attributes attributes;
    public SftpFile(DirEntry entry) {
        filename = entry.getFilename();
        longFilename = entry.getLongFilename();
        attributes = entry.getAttributes();
/*
        System.out.println("===" + filename + "/" + longFilename + "===");
        Attributes attrs = entry.getAttributes();
        System.out.println("    type = " + attrs.getType());
        System.out.println("    size = " + attrs.getSize());
        System.out.println("    owner = " + attrs.getOwner());
        System.out.println("    group = " + attrs.getGroup());
        System.out.println("    userId = " + attrs.getUserId());
        System.out.println("    groupId = " + attrs.getGroupId());
        System.out.println("    permissions = " + attrs.getPermissions());
        System.out.println("    accessTime = " + attrs.getAccessTime() + " (" + (attrs.getAccessTime() != null ? attrs.getAccessTime().toMillis() : 0) + ")<=>(" + System.currentTimeMillis() + ")");
        System.out.println("    createTime = " + attrs.getCreateTime() + " (" + (attrs.getCreateTime() != null ? attrs.getCreateTime().toMillis() : 0) + ")<=>(" + System.currentTimeMillis() + ")");
        System.out.println("    modifyTime = " + attrs.getModifyTime() + " (" + (attrs.getModifyTime() != null ? attrs.getModifyTime().toMillis() : 0) + ")<=>(" + System.currentTimeMillis() + ")");
        System.out.println("    acl = " + attrs.getAcl());
        System.out.println("    extensions = " + attrs.getExtensions());
        System.out.println("    regularFile = " + attrs.isRegularFile());
        System.out.println("    directory = " + attrs.isDirectory());
        System.out.println("    symbolicLink = " + attrs.isSymbolicLink());
        System.out.println("    other = " + attrs.isOther());
*/
    }
    public long getTimestamp() {
        return attributes.getModifyTime().toMillis();
    }
    public boolean isDirectory() {
        return attributes.isDirectory();
    }
    public String getFilename() {
        return this.filename;
    }
    public String getLongFilename() {
        return this.longFilename;
    }
}
class SftpConfig {
    public final String username;
    public final String password;
    public final String host;
    public final int port;
    public final String srcDir;
    public final String destDir;
    public SftpConfig(String username, String password, String host, int port, String srcDir, String destDir) {
        this.username = username;
        this.password = password;
        this.host = host;
        this.port = port;
        this.srcDir = srcDir;
        this.destDir = destDir;
    }
}
public class SftpTest {
    public static void main(String args[]) {
        Logger logger = LoggerFactory.getLogger(SftpTest.class);
        logger.debug("Logger is start.");

        try (SshClient client = SshClient.setUpDefaultClient()) {
            client.start();

            SftpConfig[] config = {
                new SftpConfig("YOUR_NAME", "YOUR_PASSWORD", "localhost", 22, "test", "test"),
            };

            for (int i = 0; i < config.length; ++i) {
                try (ClientSession session = client.connect(config[i].username, config[i].host, config[i].port).verify(60 * 1000).getSession()) {
                    System.out.println("session started");
                    session.addPasswordIdentity(config[i].password);
                    session.auth().verify(60 * 1000);

                    try (SftpClient sftp = SftpClientFactory.instance().createSftpClient(session)) {
                        Iterable<DirEntry> entries;
                        try (CloseableHandle handle = sftp.openDir(config[i].srcDir)) {
                            entries = sftp.listDir(handle);
                            for (DirEntry entry: entries) {
                                SftpFile file = new SftpFile(entry);
                                System.out.println(file.getFilename());
                                System.out.println(file.getLongFilename());
                TimeZone defaultTZ = TimeZone.getDefault();
                TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
                DateFormat df = new SimpleDateFormat("YYYY-MM-dd'T'HH:mm:ssZ");
                                System.out.println(df.format(file.getTimestamp()));
                                System.out.println(df.format(System.currentTimeMillis()));
                // デフォルトタイムゾーンを元に戻す。
                TimeZone.setDefault(defaultTZ);
                                String filename = file.getFilename();
                                logger.info(filename);
                                if (filename.equals(".") || filename.equals("..")) {
                                    continue;
                                }
                                if (file.isDirectory()) {
                                    continue;
                                }
                                try (InputStream is = sftp.read(config[i].srcDir + "/" + filename)) {
                                    File dir = new File(config[i].destDir);
                                    if (!dir.exists()) {
                                        dir.mkdirs();
                                    }
                                    FileOutputStream os = new FileOutputStream(new File(config[i].destDir, filename));
                                    IOUtils.copy(is, os);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                    logger.error(config[i].srcDir + "/" + filename + ": No such file", e);
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
    }

}

ビルドスクリプト

#! /bin/bash

CP=.
CP=${CP}:apache-mina-2.2.3/dist/mina-core-2.2.3.jar
CP=${CP}:apache-mina-2.2.3/lib/commons-logging-1.0.3.jar
CP=${CP}:apache-mina-2.2.3/lib/slf4j-api-1.7.36.jar
CP=${CP}:apache-sshd-2.11.0/lib/sshd-core-2.11.0.jar
CP=${CP}:apache-sshd-2.11.0/lib/sshd-common-2.11.0.jar
CP=${CP}:apache-sshd-2.11.0/lib/sshd-sftp-2.11.0.jar
CP=${CP}:apache-sshd-2.11.0/lib/slf4j-jdk14-1.7.32.jar
CP=${CP}:commons-io-2.16.1/commons-io-2.16.1.jar

javac -cp ${CP} SftpTest.java
java -cp ${CP} SftpTest

実行結果

冒頭に警告がいくつか出るのだけど、今回は割愛。

.
drwxr-xr-x    2 YOUR_NAME YOUR_NAME       27 Apr 29 03:16 .
2024-04-28T18:16:47+0000
2024-04-28T18:38:40+0000
Apr 29, 2024 3:38:40 AM SftpTest main
INFO: .
..
drwx------    3 YOUR_NAME YOUR_NAME       74 Apr 29 03:16 ..
2024-04-28T18:16:18+0000
2024-04-28T18:38:40+0000
Apr 29, 2024 3:38:40 AM SftpTest main
INFO: ..
SftpTest.java
-rwxr-x---    1 YOUR_NAME YOUR_NAME     5392 Apr 29 03:16 SftpTest.java
2024-04-28T18:16:47+0000
2024-04-28T18:38:40+0000
Apr 29, 2024 3:38:40 AM SftpTest main
INFO: SftpTest.java

参考