JavaでSFTPに接続する方法

Javaを使ったSFTPへの接続、ファイルのリストアップ、アップロード、ダウンロードの方法についてご説明します。

本記事では、SFTP サーバーに接続してやり取りするための Java コードの書き方についてご説明します。SFTPは広く使われている標準的で安全なプロトコルで、主な目的は、安全なファイルやデータ転送の提供です。サーバーへの接続を行う際には、いくつかの簡単なステップを習うだけなので、この記事が終わるころには、簡単にこれらが出来るでしょう。

必要条件

SFTP サーバーへの接続に使われる Java ライブラリはいくつかあります。ここでは、JavaのみでSSH2を実装した Jsch を使うことにします。以下の例は、JavaプロジェクトにJSCHを追加する方法のほんの一部です:

Mavenを使用:

<dependency>
    <groupId>com.github.mwiede</groupId>
    <artifactId>jsch</artifactId>
    <version>0.2.0</version>
</dependency>

Gradleを使用:

implementation 'com.github.mwiede:jsch:0.2.0'

SFTPへの接続

本記事では、SFTP サーバーに接続するために必要な情報をすべて URI 形式で格納した SFTPTOGO_URL という環境変数を使います:sftp://user:password@host.

この変数は URI.parse を使って URI の部分を抽出し、リモートサーバーのホストキーは ~/.ssh/known_hostsを使って自動的に検証されます。

SftpClient クラスのインスタンスを作成する際に、ホスト、ユーザ名、およびオプションでポート (デフォルトは 22) を渡します。パスワードで認証するには、authPassword関数を使い、公開鍵認証を使って認証するには、秘密鍵へのパスとパス文字列を authKey関数に渡します。

package com.sftptogo;

import com.jcraft.jsch.*;
import com.sftptogo.util.Util;

import java.util.Properties;
import java.util.Vector;

/**
 * A simple SFTP client using JSCH http://www.jcraft.com/jsch/
 */
public final class SftpClient {
    private final String      host;
    private final int         port;
    private final String      username;
    private final JSch        jsch;
    private       ChannelSftp channel;
    private       Session     session;

    /**
     * @param host     remote host
     * @param port     remote port
     * @param username remote username
     */
    public SftpClient(String host, int port, String username) {
        this.host     = host;
        this.port     = port;
        this.username = username;
        jsch          = new JSch();
    }

    /**
     * Use default port 22
     *
     * @param host     remote host
     * @param username username on host
     */
    public SftpClient(String host, String username) {
        this(host, 22, username);
    }

    /**
     * Authenticate with remote using password
     *
     * @param password password of remote
     * @throws JSchException If there is problem with credentials or connection
     */
    public void authPassword(String password) throws JSchException {
        session = jsch.getSession(username, host, port);
        //disable known hosts checking
        //if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
        var config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.setPassword(password);
        session.connect();
        channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();
    }


    public void authKey(String keyPath, String pass) throws JSchException {
        jsch.addIdentity(keyPath, pass);
        session = jsch.getSession(username, host, port);
        //disable known hosts checking
        //if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
        var config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.connect();
        channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();
    }
}

ファイル一覧

接続ができたので、リモート SFTP サーバー上のファイル一覧が取得できます。ラッパー関数 listFilesは、チャネルのls関数を呼び出してリモートファイルのリストを取得し、それを反復処理で各ファイルのパーミッション、ファイル名、サイズを表示します。

/**
 * List all files including directories
 *
 * @param remoteDir Directory on remote from which files will be listed
 * @throws SftpException If there is any problem with listing files related to permissions etc
 * @throws JSchException If there is any problem with connection
 */
@SuppressWarnings("unchecked")
public void listFiles(String remoteDir) throws SftpException, JSchException {
    if (channel == null) {
        throw new IllegalArgumentException("Connection is not available");
    }
    System.out.printf("Listing [%s]...%n", remoteDir);
    channel.cd(remoteDir);
    Vector<ChannelSftp.LsEntry> files = channel.ls(".");
    for (ChannelSftp.LsEntry file : files) {
        var name        = file.getFilename();
        var attrs       = file.getAttrs();
        var permissions = attrs.getPermissionsString();
        var size        = Util.humanReadableByteCount(attrs.getSize());
        if (attrs.isDir()) {
            size = "PRE";
        }
        System.out.printf("[%s] %s(%s)%n", permissions, name, size);
    }
}

ファイルのアップロード

次のステップは、ファイルのアップロードです。このクラスのuploadFileは、2つの引数と取っており、localPath(アップロードするファイル)とremotePath(サーバー上のアップロード先)です。呼び出しの例は次のようになります: client.uploadFile("./local.txt", "/remote.txt");

/**
 * Upload a file to remote
 *
 * @param localPath  full path of location file
 * @param remotePath full path of remote file
 * @throws JSchException If there is any problem with connection
 * @throws SftpException If there is any problem with uploading file permissions etc
 */
public void uploadFile(String localPath, String remotePath) throws JSchException, SftpException {
    System.out.printf("Uploading [%s] to [%s]...%n", localPath, remotePath);
    if (channel == null) {
        throw new IllegalArgumentException("Connection is not available");
    }
    channel.put(localPath, remotePath);
}

ファイルのダウンロード

最後は、ファイルのダウンロードだけです。 downloadFile関数は、2つの引数を取っており、remotePath(ダウンロードするファイル)とlocalPath(ファイルのダウンロード先)です。client.downloadFile("/remote.txt", "./download.txt");のように関数を呼び出すことになります。

/**
 * Download a file from remote
 *
 * @param remotePath full path of remote file
 * @param localPath  full path of where to save file locally
 * @throws SftpException If there is any problem with downloading file related permissions etc
 */
public void downloadFile(String remotePath, String localPath) throws SftpException {
    System.out.printf("Downloading [%s] to [%s]...%n", remotePath, localPath);
    if (channel == null) {
        throw new IllegalArgumentException("Connection is not available");
    }
    channel.get(remotePath, localPath);
}

まとめ

これで完了です!もし、このプログラムを最初から最後まで通して実行したい場合は、プロジェクトの構築と実行方法について、こちらの説明に従ってください。

package com.sftptogo;

import com.jcraft.jsch.*;
import com.sftptogo.util.Util;

import java.util.Properties;
import java.util.Vector;

/**
 * A simple SFTP client using JSCH http://www.jcraft.com/jsch/
 */
public final class SftpClient {
    private final String      host;
    private final int         port;
    private final String      username;
    private final JSch        jsch;
    private       ChannelSftp channel;
    private       Session     session;

    /**
     * @param host     remote host
     * @param port     remote port
     * @param username remote username
     */
    public SftpClient(String host, int port, String username) {
        this.host     = host;
        this.port     = port;
        this.username = username;
        jsch          = new JSch();
    }

    /**
     * Use default port 22
     *
     * @param host     remote host
     * @param username username on host
     */
    public SftpClient(String host, String username) {
        this(host, 22, username);
    }

    /**
     * Authenticate with remote using password
     *
     * @param password password of remote
     * @throws JSchException If there is problem with credentials or connection
     */
    public void authPassword(String password) throws JSchException {
        session = jsch.getSession(username, host, port);
        //disable known hosts checking
        //if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
        var config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.setPassword(password);
        session.connect();
        channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();
    }


    public void authKey(String keyPath, String pass) throws JSchException {
        jsch.addIdentity(keyPath, pass);
        session = jsch.getSession(username, host, port);
        //disable known hosts checking
        //if you want to set knows hosts file You can set with jsch.setKnownHosts("path to known hosts file");
        var config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.connect();
        channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();
    }

    /**
     * List all files including directories
     *
     * @param remoteDir Directory on remote from which files will be listed
     * @throws SftpException If there is any problem with listing files related to permissions etc
     * @throws JSchException If there is any problem with connection
     */
    @SuppressWarnings("unchecked")
    public void listFiles(String remoteDir) throws SftpException, JSchException {
        if (channel == null) {
            throw new IllegalArgumentException("Connection is not available");
        }
        System.out.printf("Listing [%s]...%n", remoteDir);
        channel.cd(remoteDir);
        Vector<ChannelSftp.LsEntry> files = channel.ls(".");
        for (ChannelSftp.LsEntry file : files) {
            var name        = file.getFilename();
            var attrs       = file.getAttrs();
            var permissions = attrs.getPermissionsString();
            var size        = Util.humanReadableByteCount(attrs.getSize());
            if (attrs.isDir()) {
                size = "PRE";
            }
            System.out.printf("[%s] %s(%s)%n", permissions, name, size);
        }
    }

    /**
     * Upload a file to remote
     *
     * @param localPath  full path of location file
     * @param remotePath full path of remote file
     * @throws JSchException If there is any problem with connection
     * @throws SftpException If there is any problem with uploading file permissions etc
     */
    public void uploadFile(String localPath, String remotePath) throws JSchException, SftpException {
        System.out.printf("Uploading [%s] to [%s]...%n", localPath, remotePath);
        if (channel == null) {
            throw new IllegalArgumentException("Connection is not available");
        }
        channel.put(localPath, remotePath);
    }

    /**
     * Download a file from remote
     *
     * @param remotePath full path of remote file
     * @param localPath  full path of where to save file locally
     * @throws SftpException If there is any problem with downloading file related permissions etc
     */
    public void downloadFile(String remotePath, String localPath) throws SftpException {
        System.out.printf("Downloading [%s] to [%s]...%n", remotePath, localPath);
        if (channel == null) {
            throw new IllegalArgumentException("Connection is not available");
        }
        channel.get(remotePath, localPath);
    }

    /**
     * Delete a file on remote
     *
     * @param remoteFile full path of remote file
     * @throws SftpException If there is any problem with deleting file related to permissions etc
     */
    public void delete(String remoteFile) throws SftpException {
        System.out.printf("Deleting [%s]...%n", remoteFile);
        if (channel == null) {
            throw new IllegalArgumentException("Connection is not available");
        }
        channel.rm(remoteFile);
    }

    /**
     * Disconnect from remote
     */
    public void close() {
        if (channel != null) {
            channel.exit();
        }
        if (session != null && session.isConnected()) {
            session.disconnect();
        }
    }
}

Javaを使ったSFTPへの接続ができました。お疲れ様でした。

その他のコードサンプルはGithubでご確認ください。