기본 콘텐츠로 건너뛰기

SFTP 파일 업로드 및 다운로드

🔒 Java JSch 활용: SFTP 파일 업로드/다운로드 유틸리티 완벽 분석

JSch(Java Secure Channel)는 Java에서 SSH2를 구현한 라이브러리로, 안전한 SFTP 통신을 가능하게 합니다. 파일 다운로드, 업로드, 그리고 파일 전송에 필요한 디렉터리 생성 로직까지 포함된 핵심 유틸리티 코드를 분석해 보겠습니다.


1. 🔑 SFTP 접속 공통 로직

다운로드 및 업로드 함수 모두 공통적으로 **`GetProperties`**를 사용하여 접속 정보를 불러온 후, JSch를 통해 SSH 세션을 설정합니다. 특히 `StrictHostKeyChecking`을 `no`로 설정하는 부분은 최초 접속 시 호스트 키 검사를 생략하여 편리하게 접속하도록 합니다 (보안상 운영 환경에서는 신중하게 고려해야 할 설정입니다).


// JSch 초기화 및 세션 설정
JSch jsch = new JSch();
session = jsch.getSession(ID, IP, Integer.parseInt(PORT));
session.setPassword(PWD);
// StrictHostKeyChecking 설정
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();

// SFTP 채널 열기
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
        

2. ⬇️ 파일 다운로드 로직 분석 (`download` 메소드)

클라이언트의 `BufferedOutputStream`으로 SFTP 서버의 파일을 직접 복사하는 방식입니다.


public static void download(String filePath, String fileName, BufferedOutputStream bos) throws Exception {
    // ... (접속 로직 생략) ...
    try {
        // 원격 디렉터리 이동 및 블랙리스트 필터링 (GetProperties.filePathBlackList)
        channelSftp.cd(GetProperties.filePathBlackList(PATH));
        
        // 파일을 InputStream으로 가져와 클라이언트의 OutputStream으로 복사
        bis = new BufferedInputStream(
            channelSftp.get(GetProperties.filePathBlackList(PATH) + fileName));
        
        // FileCopyUtils (Spring 유틸리티로 추정)를 사용하여 Stream 복사
        FileCopyUtils.copy(bis, bos);
        bos.flush();
    } catch (Exception ex) {
        // ★ 파일이 없을 경우 (예외 발생 시) 대체 이미지 다운로드 시도
        bis = new BufferedInputStream(channelSftp.get("/media/smama/2018/noimage1.jpg"));
        ex.printStackTrace();
    } finally {
        // 자원 해제 (필수)
        bis.close();
        bos.close();
        session.disconnect();
    }
}
            
**핵심 특징:** 파일을 찾지 못해 예외가 발생했을 때(Catch 블록), 특정 경로에 있는 **`noimage1.jpg` 파일을 대신 다운로드**하도록 구현되어 있어 사용자에게 빈 이미지를 제공하지 않는다는 장점이 있습니다. 다만, `finally` 블록에서 `bis.close()` 시 예외 발생 여부에 관계없이 `bis`가 초기화되지 않았을 가능성도 있으므로 주의해야 합니다.

3. ⬆️ 파일 업로드 및 디렉터리 생성 로직 분석 (`upload` & `mkdirs`)

파일 업로드는 `InputStream`을 받아와 지정된 원격 경로에 파일을 저장합니다. 이때 원격 경로가 존재하지 않으면 **`mkdirs`**를 호출하여 디렉터리를 생성하는 로직이 핵심입니다.

3.1. 업로드 로직 (`upload`)


// ... (접속 로직 생략) ...
try {
    SftpATTRS attrs = null;
    try {
        attrs = channelSftp.stat(PATH); // 1. 경로 존재 여부 확인
    } catch (Exception e) {
        // 경로가 없으면 e.printStackTrace() 후 attrs = null 상태 유지
    }

    if (attrs != null) {
        // 2. 경로가 존재하면 해당 디렉터리로 이동
        channelSftp.cd(PATH);
    } else {
        // 3. 경로가 존재하지 않으면 mkdirs 함수로 디렉터리 생성 후 이동
        mkdirs(PATH, channelSftp);
        channelSftp.cd(PATH);
    }
    
    // 4. InputStream으로부터 파일 업로드
    channelSftp.put(is, fileName);
    result = 1;
} catch (Exception ex) {
    ex.printStackTrace();
}
// ... (세션 disconnect 로직은 생략됨, 추가 필요)
        

3.2. 디렉터리 생성 로직 (`mkdirs`)

FTP/SFTP에서는 한 번에 여러 레벨의 디렉터리를 생성할 수 없습니다. 이 `mkdirs` 함수는 **재귀 호출**을 사용하여 상위 디렉터리부터 차례대로 생성합니다.


private static void mkdirs(String directory, ChannelSftp c) throws IOException, SftpException {
    try {
        SftpATTRS att = c.stat(directory); // 1. 경로의 존재 여부 및 타입 확인
        if (att != null && att.isDir()) {
            return; // 이미 폴더가 존재하면 종료
        }
    } catch (SftpException ex) {
        // 2. 폴더가 없어서 예외가 발생하면
        if (directory.indexOf('/') != -1) {
            // 상위 디렉터리를 먼저 생성하도록 재귀 호출
            mkdirs(directory.substring(0, directory.lastIndexOf('/')), c);
        }
        // 현재 디렉터리 생성
        c.mkdir(directory);
    }
}
        

4. 🌟 요약 및 권장 사항

이 유틸리티는 JSch를 사용하여 보안성이 높은 파일 전송을 구현했으며, 특히 디렉터리 자동 생성 기능과 다운로드 오류 시 대체 이미지 제공 로직이 포함되어 있어 실무에 바로 적용하기 좋습니다.

**권장 사항:** `upload` 메소드에는 `download` 메소드와 마찬가지로 `finally` 블록에서 `session.disconnect()`, `channel.disconnect()` 등 자원 해제 로직을 추가하여 안전성을 높이는 것이 좋습니다.

© 2025 Java 개발자 가이드.

댓글

이 블로그의 인기 게시물

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 (Request Parameter 전체보기)

Java Servlet Request Parameter 완전 정복 — GET/POST 모든 파라미터 확인 & 디버깅 예제 Java Servlet Request Parameter 완전 정복 웹 애플리케이션에서 클라이언트로부터 전달되는 Request Parameter 를 확인하는 것은 필수입니다. 이 글에서는 Java Servlet 과 JSP 에서 GET/POST 요청 파라미터를 전체 출력하고 디버깅하는 방법을 다양한 예제와 함께 소개합니다. 1. 기본 예제: getParameterNames() 사용 Enumeration<String> params = request.getParameterNames(); System.out.println("----------------------------"); while (params.hasMoreElements()){ String name = params.nextElement(); System.out.println(name + " : " + request.getParameter(name)); } System.out.println("----------------------------"); 위 코드는 요청에 포함된 모든 파라미터 이름과 값을 출력하는 기본 방법입니다. 2. HTML Form과 연동 예제 <form action="CheckParamsServlet" method="post"> 이름: <input type="text" name="username"><br> 이메일: <input type="email" name="email"><b...

PostgreSQL 달력(일별,월별)

SQL 팁: GENERATE_SERIES로 일별, 월별 날짜 목록 만들기 SQL 팁: GENERATE_SERIES 로 일별, 월별 날짜 목록 만들기 데이터베이스에서 통계 리포트를 작성하거나 비어있는 날짜 데이터를 채워야 할 때, 특정 기간의 날짜 목록이 필요할 수 있습니다. PostgreSQL과 같은 데이터베이스에서는 GENERATE_SERIES 함수를 사용하여 이 작업을 매우 간단하게 처리할 수 있습니다. 1. 🗓️ 일별 날짜 목록 생성하기 2020년 1월 1일부터 12월 31일까지의 모든 날짜를 '1 day' 간격으로 생성하는 쿼리입니다. WITH date_series AS ( SELECT DATE(GENERATE_SERIES( TO_DATE('2020-01-01', 'YYYY-MM-DD'), TO_DATE('2020-12-31', 'YYYY-MM-DD'), '1 day' )) AS DATE ) SELECT DATE FROM date_series 이 쿼리는 WITH 절(CTE)을 사용하여 date_series 라는 임시 테이블을 만들고, GENERATE_SERIES 함수로 날짜를 채웁니다. 결과 (일별 출력) 2. 📅 월별 날짜 목록 생성하기 동일한 원리로, 간격을 '1 MONTH' 로 변경하면 월별 목록을 생성할 수 있습니다. TO...

CSS로 레이어 팝업 화면 가운데 정렬하는 방법 (top·left·transform 완전 정리)

레이어 팝업 센터 정렬, 이 코드만 알면 끝 (CSS 예제 포함) 이벤트 배너나 공지사항을 띄울 때 레이어 팝업(center 정렬) 을 깔끔하게 잡는 게 생각보다 어렵습니다. 화면 크기가 변해도 가운데에 고정되고, 모바일에서도 자연스럽게 보이게 하려면 position , top , left , transform 을 정확하게 이해해야 합니다. 이 글에서는 아래 내용을 예제로 정리합니다. 레이어 팝업(center 정렬)의 기본 개념 자주 사용하는 position: absolute / fixed 정렬 방식 질문에서 주신 스타일 top: 3.25%; left: 50%; transform: translateX(-50%) 의 의미 실무에서 바로 쓰는 반응형 레이어 팝업 HTML/CSS 예제 1. 레이어 팝업(center 정렬)이란? 레이어 팝업(레이어 팝업창) 은 새 창을 띄우는 것이 아니라, 현재 페이지 위에 div 레이어를 띄워서 공지사항, 광고, 이벤트 등을 보여주는 방식을 말합니다. 검색엔진(SEO) 입장에서도 같은 페이지 안에 HTML이 존재 하기 때문에 팝업 안의 텍스트도 정상적으로 인덱싱될 수 있습니다. 즉, “레이어 팝업 센터 정렬”, “레이어 팝업 만드는 방법”과 같이 관련 키워드를 적절히 넣어주면 검색 노출에 도움이 됩니다. 2. 질문에서 주신 레이어 팝업 스타일 분석 질문에서 주신 스타일은 다음과 같습니다. <div class="layer-popup" style="width:1210px; z-index:9001; position:absolute; top:3.25%; left:50%; transform:translateX(-50%);"> 레이어 팝업 내용 <...