Java 로 SFTP 에 연결하는 방법

Java를 사용하여 SFTP에 연결, 파일 목록, 업로드 및 다운로드 방법에 대해 설명합니다.

이 기사에서는 SFTP 서버에 연결하고 상호 작용하는 Java 코드를 작성하는 방법에 대해 설명합니다. SFTP는 널리 사용되는 표준적이고 안전한 프로토콜이며, 주요 목적은 안전한 파일 및 데이터 전송을 제공하는 것입니다. 서버에 연결할 때 필요한 몇 가지 간단한 절차를 배울 뿐이므로 이 기사를 끝까지 읽는 것으로 간단하게 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은 두 개의 인수로 사용되며 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함수는 remotePath(다운로드 할 파일)와 localPath(파일이 다운로드 될 위치)의 2 개의 인수를 취하고 있습니다. 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에 연결할 수 있었습니다. 축하합니다!

보안성과 안정성을 극대화한 SFTP To Go
SFTP To Go는 관리형 SFTP/FTPS/S3를 서비스 형태로 제공하며, 최고의 안정성, 보안, 가용성, 1분 만에 설치가 가능하며, 모든 규모의 기업에 적합합니다.
지금 바로 SFTP To Go를 사용해 보세요!

다른 코드 샘플은Github에서 꼭 확인하십시오.