sftp를 활용할 프로젝트가 생겼다. 대충 개발망에서는 라이브러리가 없으니까, 외부 개인 놋북에서 샘플소스코드를 작성 하고 라이브러리를 반입하기로 했다. 이하는 대충 짜보고 찾아보고 제미니에게 물어본 소스코드.

 

Springboot 2.7.8, jcraft jsch 라이브러리는 0.1.55 버전 (0.1.54 버전도 동작됨을 확인함) 이다.

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.jcraft/jsch -->
<dependency>
    <groupId>com.jcraft</groupId>
    <artifactId>jsch</artifactId>
    <version>0.1.55</version>
</dependency>
cs

 

1. SftpUtil.java

유틸과 같이 target Path , target FileName 정도만 입력받으려고 구성했다. 어차피 sftp 접속정보는 application.properties에 작성되어있을거니까.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package com.test.common.utils;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
 
public class SftpUtil {
    private static final String SESSION_CONFIG_STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
    private static final Logger logger = LoggerFactory.getLogger(SftpUtil.class);
 
    private String host;
    private String username;
    private String password;
    
    private int port;
    private int timeout = 15000;
 
    public SftpUtil(String host, String username, String password, String port) {
        this.host = host;
        this.username = username;
        this.password = password;
        this.port = Integer.parseInt(port);
    }
 
    private ChannelSftp createSftp() throws Exception {
        JSch jsch = new JSch();
 
        Session session = createSession(jsch, host, username, port);
        session.setPassword(password);
        session.connect(timeout);
 
        Channel channel = session.openChannel("sftp");
        channel.connect(timeout);
 
        return (ChannelSftp) channel;
    }
 
    private Session createSession(JSch jsch, String host, String username, int port) throws Exception {
        Session session = null;
 
        if (port <= 0) {
            session = jsch.getSession(username, host);
        } else {
            session = jsch.getSession(username, host, port);
        }
 
        session.setConfig(SESSION_CONFIG_STRICT_HOST_KEY_CHECKING, "no");
        logger.info("===== session ::::: " + session);
 
        return session;
    }
 
    private void disconnect(ChannelSftp sftp) {
        try {
            if (sftp != null) {
                if (sftp.isConnected()) {
                    sftp.disconnect();
                } else if (sftp.isClosed()) {
                }
                if (null != sftp.getSession()) {
                    sftp.getSession().disconnect();
                }
            }
        } catch (JSchException e) {
            e.printStackTrace();
        }
    }
 
    // Rest에서 호출함
    public boolean uploadFile(String targetPath, String fileName, File file) throws Exception {
        FileInputStream fis = new FileInputStream(file);
        ChannelSftp sftp = this.createSftp();
        try {
            sftp.cd(targetPath);
            sftp.put(fis, fileName);
            return true;
        } catch (Exception e) {
            logger.debug("==== exception ::::: " + e.getMessage());
            throw new Exception("Upload File failure");
        } finally {
            this.disconnect(sftp);
        }
    }
 
    // Rest에서 호출함
    public boolean downloadFile(String remoteFilePath, String localSavePath) throws Exception {
        ChannelSftp sftp = this.createSftp();
        try {
            sftp.get(remoteFilePath, localSavePath);
            return true;
        } catch (Exception e) {
            logger.debug("==== exception ::::: " + e.getMessage());
            throw new Exception("Download File failure");
        } finally {
            this.disconnect(sftp);
        }
    }
 
}
cs

 

소스코드가 좀 길기는 한데, 어차피 Upload / Download는 공통적인 connection / disconnection 과정을 거치기에 상관은 없다.

 

호출부는 다음과 같다.

 

2. Rest Controller 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@PostMapping("/testSftpUpload")
public String testSftpUpload(@RequestBody String a) {
    String result = "";
    
    SftpUtil ftp = new SftpUtil( sftpHost, sftpUsername, sftpPassword, sftpPort );
    File file = new File("C:\\Users\\KimFish\\Desktop\\TEST.csv");
    try {
        ftp.uploadFile("/C:/sftp""1234.csv", file);
    } catch (Exception e) {            
        e.printStackTrace();
    }
    
    return result;        
}
 
@PostMapping("/testSftpDownload")
public String testSftpDownload(@RequestBody String a) {
    String result = "";
    
    SftpUtil ftp = new SftpUtil( sftpHost, sftpUsername, sftpPassword, sftpPort );
    File file = new File("C:\\Users\\KimFish\\Desktop\\TEST.csv");
    try {
        ftp.downloadFile("/C:/sftp/1234.csv""/C:/Users/KimFish/Desktop/test1234.csv");
    } catch (Exception e) {            
        e.printStackTrace();
    }
    
    return result;        
}
cs

 

이정도면 충분한듯.

각각의 호출부는 host , port , userid , userpw 모두 입력받고, 메서드에 따라 파일 및 경로 등을 같이 인자값으로 던지게 된다. 일단은 이정도로 되었다... 

 

 

*** 알아둘 점 ***

리눅스에서 디렉토리 구분자는 / 를 사용하는데 반해, 윈도우에서는 디렉토리 구분자를 \를 써야 한다.

그런데 ftp(sftp포함)에서는 무조건 /를 사용해야하니 저렇게 작성된 것. 그에 비해 File 메서드 같은 경우는 Windows OS에서 구동되는 자바 프로그램이기 때문에 \ 를 사용함을 볼 수 있다.

이렇게 또 하나 배우는구만... 어차피 리눅스 드가면 슬래쉬 쓸거...

블로그 이미지

김생선

세상의 모든것을 어장관리

,