FTP(File Transfer Protocol) (2) : 클라이언트 FTP 서버 구축 WITH JAVA -connect && login

__Y0Connell__·2024년 4월 21일

FTP_SERVICE

목록 보기
2/2

사용 라이브러리 : commons-net:commons-net:3.6

FTP 클라이언트 서버를 구축하기 위해서, 먼저 필요한 라이브러리를 추가해야한다.
내가 선택한 라이브러리는 commons-net:commons-net이다.


* Apache Commons Net은 FTP, SMTP, POP3, IMAP 등과 같은 다양한 네트워크 프로토콜을 구현하는 데 사용될 수 있습니다. FTP 클라이언트를 구축하거나, 네트워크 상에서 파일 전송 및 통신을 위한 다양한 작업을 수행할 때 유용합니다.


Maven을 사용한다면 아래와 같이 : 라이브러리를 추가 해준다.

pom.xml

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>

나는 gradle을 사용했기에 :

build.gradle에 아래와 같이 추가해줬다.

dependencies {
    implementation 'commons-net:commons-net:3.6'
}

접속 / 로그인

  1. 접속

    접속 하기 전 연결되어 있지 확인을 하고

✅ ftpClient.isConnected();

되어 있지 않으면, 접속을 시도한다.

✅ ftpClient.connect(host,port);

연결하고 데이터를 기다리는 동안 무기한 대기하는 것을 방지하기 위해 timeout을 걸어준다.
지정된 시간 내에 데이터가 도착하지 않으면 작업이 SocketTimeoutException을 던진다.

✅ ftpClient.setSoTimeout(1000 60 60);


      public boolean connect(
              FTPClient ftpClient,
              String host,
              int port
      ){
          boolean isConnected = ftpClient.isConnected(); // 연결되어있는 확인
          if(!isConnected){
              try
              {
                  ftpClient.connect(host,port); //접속 시도
                  String replyConnect = ftpClient.getReplyString(); 
                  log.info("CONNECT REPLY : {} " , replyConnect);
                  ftpClient.setSoTimeout(1000 * 60 * 60); 
                  // 응답 대기 시간 1 hour connect
              } catch (SocketException e) {
                  throw new RuntimeException(e);
              } catch (IOException e) {
                  throw new RuntimeException(e);
              }
          }
          return isConnected;
      }

  1. 로그인

✅ enterLocalPassiveMode();

서버가 클라이언트에게 데이터 포트를 제공하고, 클라이언트가 해당 포트로 연결을 생성하게 해준다.

**액티브 모드(Active Mode)가 따로 있는데, 서버는 클라이언트의 연결을 받아들이고, 데이터를 클라이언트에게 직접 전송하는 방법인데, 보통 passiveMode()를 쓴다고 한다.

✅ setKeepAlive(true) :

나 아직 살아있어요~ 라고 클라이언트에게 알려주는 것,
즉 파일이 전송되지 않는 시간들에도 로그인 후 활성 상태라는 것을 전해주는 것이다.

setFileType(FTP.BINARY_FILE_TYPE):
파일을 바이너리타입으로 송수신하겠다고 지정해준다.
binary type 이외에도 ftp는 ASCII 모드를 제공해준다고 한다.
하지만 ASCII 모드는 서로 다른 운영 체제 간에 변환이 이루어지기 때문에,
원본 파일을 보존하고 싶다면, 바이너리를 추천한다.

   public boolean logIn(FTPClient ftpClient, String loginId, String loginPw) throws IOException {

       boolean isLogIn = ftpClient.login(loginId, loginPw);
       String replyLogIn = ftpClient.getReplyString();
       log.info("CONNECT REPLY : {} ", replyLogIn);

       if (isLogIn) {
           log.info("LOGIN SUCCESS : ID - {}", loginId);
           ftpClient.enterLocalPassiveMode(); // passive mode
           ftpClient.setKeepAlive(true); // alert " i am alive "
           ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // as original data is
           ftpClient.setSoTimeout(1000 * 60 * 60); // 1 hour login
           return true;
       } else {
           log.info("LOGIN FAIL : ID - {}, PW - {}", loginId, loginPw);
           ftpClient.disconnect(); // 로그인 실패시 disconnect
           return false;
       }
   }

보통 connect와 login을 함께 쓰기 때문에,

connect 와 login은 util 파일에 넣어둔 반면, 해당 connect 와 login이 한번에 이루어지는 로직을 service에 추가해주었다.

FtpUserVo는 ftp 접근 정보 / user 정보 / 연결,로그인 상태를 저장해두고 있도록 했다.



    public void connectNlogin(FtpUserVo userVo) throws IOException {
        boolean connectNlogin = ftpUtil.connect(ftpClient.ftpClient(),userVo.getHost(),userVo.getPort());
        if(connectNlogin){
            userVo.setConnect(true);
            connectNlogin = ftpUtil.logIn(ftpClient.ftpClient(),userVo.getLoginId(),userVo.getLoginPw());
            if(connectNlogin){
                userVo.setLogin(true);
                log.info("CONNECT : {} / LOGIN : {}", userVo.isConnect() , userVo.isLogin());
            }
        }
    }

SERVICE에서 편하게 들고 다닐 수 있도록 FtpUserVo도 하나 만들어줬다.

@Builder
@Data
public class FtpUserVo {
    private boolean isConnect;
    private boolean isLogin;
    private String loginId;
    private String loginPw;
    private int port;
    private String host;

}

FtpUserVo의 generate를 따로 만들어 주었고,

ftp 접근 정보, user 정보는 enum으로 따로 관리하는 형태로 만들어두어, ftp 이름과 user 이름을 받기만 해도 나머지 부분들이 자동 setting 될 수 있도록 만들어 두었다.

ftp 정보와 user 정보의 경우 yml에서 따로 관리해주는 것도 좋다.

    public FtpUserVo generateFtpUserVo(String userNm,String dstFtp){
        FtpEnum ftpEnum = FtpEnum.valueOf(dstFtp);
        FtpUserEnum ftpUserEnum = FtpUserEnum.valueOf(userNm);

        FtpUserVo userVo = FtpUserVo.builder()
                .host(ftpEnum.getHost())
                .port(ftpEnum.getPort())
                .loginId(ftpUserEnum.getLoginID())
                .loginPw(ftpUserEnum.getLoginPW())
                .build();

        log.info("USER ID : {}" , userVo.getLoginId());
        return userVo;
    }
  • user 이름을 받아서 id와 pw를 정보를 찾아 올 수 있는 enum
public enum FtpUserEnum {
    EASY_USER_FTP("user_ID","user_PW");
    private final String loginID;
    private final String loginPW;

    public String getLoginID() {
        return loginID;
    }

    public String getLoginPW() {
        return loginPW;
    }

    FtpUserEnum(String loginID, String loginPW) {
        this.loginID = loginID;
        this.loginPW = loginPW;
    }
}
  • ftp 이름을 받아서 host와 port를 정보를 찾아 올 수 있는 enum
public enum FtpEnum {
    EASY_FTP("127.0.0.1",21);
    private final String host;
    private final int port;

    FtpEnum(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }
}

마지막으로 controller에서 해당 접속/로그인 기능을 call 할 수 있는 api를 작성해두었다.

    @GetMapping(value="/connectNLogin")
    public String connectNLogin(
            @RequestParam(value ="접근 USER",defaultValue = "USER") String userNm, 
            @RequestParam(value ="FTP 정보",defaultValue = "FTP_INFO") String dstFtp) throws IOException, InterruptedException {
           // userVo를 만들고  
        FtpUserVo userVo = ftpService.generateFtpUserVo(userNm,dstFtp);
          // 접속 및 로그인 시도한다.
        ftpService.connectNlogin(userVo);
        return "result";
    }

💻 코드는 아래의 깃에 올려두었습니다.

https://github.com/yoconnell/ftp-service

profile
되는건 다 하기 / 하는건 다 되기

0개의 댓글