본문 바로가기
JSP

[JSP]파일 업로드, 파일 다운로드(jakarta)

by Coarti 2024. 6. 1.

이클립스 재단으로 이관된 JavaEE는 JakartaEE로 이름이 변경되었다.
Tomcat 10 이후버전으로 구현시 이전과 차이점이 있어
이에 라이브러리 버전과 사용법을 정리 하고자 한다.


  1. 프로젝트 환경
  2. 라이브러리
  3. DB Table
  4. 구조
  5. 업로드
    • 단건
    • 다건
  6. 다운로드

 


프로젝트 환경

항목버전
JDKJava 17
WASTomcat 10
DBMySQL 8.0.32
Servlet5.0

라이브러리

    <dependencies>
    ...
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.2.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>
    ...
    </dependencies>

 

Jakarta Servlet API• Java 웹 애플리케이션을 개발할 때 필요한 서블릿과
  관련된 클래스 및 인터페이스를 정의한 API
• HTTP 요청 처리 및 응답 생성
MySQL Connector/J• MySQL DB와의 연결을 위한 JDBC 드라이버
• Java 애플리케이션에서 MySQL DB 접속 및 SQL 쿼리 실행
Apache Commons IO• Java의 I/O 작업을 쉽게 처리할 수 있는 유틸리티 라이브러리
• 파일 처리 관련 작업 수행 시 사용

DB Table

테이블 이름: File

FieldTypeNullKeyDefaultExtra
idint unsignedNOPRI auto_increment
original_namevarchar(100)NO   
saved_namevarchar(255)YES   
saved_pathvarchar(255)NO   
extvarchar(10)NO   
sizebigintNO 0 

구조

파일 구성

 
 


업로드 동작 순서

  1. 파일을 업로드 디렉토리 경로 가져오기
  2. 파일 업로드 후 FileVO 반환
  3. FileVO에 담긴 파일 정보 DB에 저장

다운로드 동작 순서

  1. 요청된 파일 번호로 파일 조회
  2. 다운로드

 
 
 
업로드, 다운로드에서 공용으로 사용할 클래스를 먼저 소개한다.
 
config.properties
업로드를 통해 파일을 저장할 경로
본인이 원하는 경로를 작성해도 된다
리눅스도 가능하다

saved.directory=C:/project/kb/jsp/upload

 
 
DBUtil

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 데이터베이스 연결과 관련된 유틸리티 메소드를 제공하는 클래스
 */
public class DBUtil {
    static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    static final String DB_URL = "jdbc:mysql://localhost:3306/테이블이름";
    static final String USER = "root";
    static final String PASS = "비밀번호";

    // JDBC 드라이버 로드
    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    /**
     * 데이터베이스 연결을 생성하고 반환
     *
     * @return 데이터베이스와의 연결
     * @throws SQLException 데이터베이스 연결 생성 중 오류 발생 시
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(DB_URL, USER, PASS);
    }
    /**
     * 주어진 자원들을 안전하게 종료
     * 주로 데이터베이스 연결, statement, resultset 등을 닫는 데 사용
     *
     * @param resources 닫을 자원들
     */
    public static void release(AutoCloseable... resources) {
        for (AutoCloseable resource : resources) {
            if (resource != null) {
                try {
                    resource.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 
FileUtil

import com.cg.simplejsp._8_file.vo.FileVO;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.Part;
import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.*;

public class FileUtil {

	// 설정파일에서 업로드 위치 가져오기
    public static String getSavedFileDirectoryPath() {
        Properties p = new Properties();
        try (InputStream input = FileUtil.class
                .getClassLoader().getResourceAsStream("config.properties")) {
            p.load(input);
            return p.getProperty("saved.directory");
        } catch (IOException e) {
            e.printStackTrace();
            return null; // 또는 기본값 반환
        }
    }

    // 업로드할 디렉토리 반환
    public static File getUploadDirectory() {
        String dirPath = getSavedFileDirectoryPath();

        // 파일을 upload 할 directory 생성
        File uploadDir = new File(dirPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        return uploadDir;
    }

	// request 에서 넘어온 파일 정보로 파일 저장 후 FileVO 반환(단건)
    public static FileVO handleUploadFile(HttpServletRequest request, File uploadDir) throws ServletException, IOException {
        // form 에서 파일 가져오기, 파일 처리
        Part filePart = request.getPart("upload_file"); // form 태그 안에 있던 name 속성
        if (filePart.getSize() > 0) {
            return saveFileAndGetVo(filePart, uploadDir);
        }
        return null;
    }

	// request 에서 넘어온 파일 정보로 파일 저장 후 List<FileVO> 반환(다건)
    public static List<FileVO> handleUploadFiles(HttpServletRequest request, File uploadDir) throws ServletException, IOException {
        // form 에서 파일 가져오기, 파일 처리
        Collection<Part> parts = request.getParts();
        List<FileVO> files = new ArrayList<>();
        for (Part part : parts) {
            if (part.getName().equals("upload_file") && part.getSize() > 0)// 파일인 경우
                files.add(saveFileAndGetVo(part, uploadDir));
        }
        return files;

    }

	//파일 저장
    private static FileVO saveFileAndGetVo(Part part, File uploadDir) throws IOException {
        String originalName = part.getSubmittedFileName();// 파일 원본 이름
        String extension = FilenameUtils.getExtension(originalName); // 확장자
        String fileName = originalName.substring(0, originalName.indexOf("."));// 확장자 제외 파일 이름
        String savedName = UUID.randomUUID() + "_" + fileName; // 중복 방지
        File file = new File(uploadDir, savedName + "." + extension); // File(File parent, File child), uploadDir + saveName(확장자 포함) 가진 파일(사실상 String 임)

        // 파일 저장
        try (InputStream input = part.getInputStream(); // 파일 데이터
             OutputStream output = Files.newOutputStream(file.toPath()) // 저장 위치
        ) {
            input.transferTo(output); // output 경로에 input 에 담긴 데이터를 내보냄
        }
        return new FileVO(fileName, savedName, file.getParent(), extension, part.getSize());
    }
}

 
FileVO

public class FileVO {
    private long fileId; // 식별자
    private String originalName; // 파일 원본 이름
    private String savedName; // 서버에 저장되는 파일 이름
    private String savedPath; // 서버에 파일이 저장되는 경로
    private String ext; // 확장자
    private long size; // 파일 크기

    // 파일 조회용
    public FileVO(long fileId, String originalName, String savedName, String savedPath, String ext) {
        this.fileId = fileId;
        this.originalName = originalName;
        this.savedName = savedName;
        this.savedPath = savedPath;
        this.ext = ext;
    }

    // 파일 저장용
    public FileVO(String originalName, String savedName, String savedPath, String ext, long size) {
        this(0, originalName, savedName, savedPath, ext);
        this.size = size;
    }

	// getter and setter
    
}

 

업로드

단건

upload.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<center>
    <h2> 파일 업로드 </h2>
    <form action="/upload.do" method="post" enctype="multipart/form-data">
        <input type="file" name="upload_file" />
        <input type="submit" value="저장" />
    </form>
</center>
</body>
</html>

 
FileUpload

import com.cg.simplejsp._8_file.dao.FileDAO;
import com.cg.simplejsp._8_file.util.FileUtil;
import com.cg.simplejsp._8_file.vo.FileVO;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.IOException;

/**
 * 파일 업로드 HTTP 요청을 처리하는 서블릿
 */
@MultipartConfig(
        fileSizeThreshold = 1024 * 1024, // 파일이 메모리에 저장되기 시작하는 임계치, 임계치보다 클 경우 디스크에 저장
        maxFileSize = 1024 * 1024 * 5, // 각 업로드 파일의 최대 허용 크기
        maxRequestSize = 1024 * 1024 * 10 // 전체 요청의 최대 크기
)
@WebServlet("/upload.do")
public class FileUploadController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        RequestDispatcher dispatcher = request.getRequestDispatcher("file/upload.jsp");
        dispatcher.forward(request, response);

    }


    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }

    private void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        // 파일을 업로드할 디렉토리 경로 가져오기
        File uploadDirectory = FileUtil.getUploadDirectory();

        // 파일 업로드 후 파일 정보 반환
        FileVO file = FileUtil.handleUploadFile(request, uploadDirectory);

        // 파일 정보를 DB에 저장
        if (file != null) {
            FileDAO dao = new FileDAO();
            dao.insert(file);
        }

        response.sendRedirect("/upload.do");
    }

}

 

다건

단건처리 부분의 일부분을 변경하면 된다.
 
uploads.jsp(위 파일과 이름이 다름)

...
<form action="/uploads.do" method="post" enctype="multipart/form-data">
    <% for (int i = 0; i < 3; i++) {%>
    <input type="file" name="upload_file"/>
    <%}%>
    <input type="submit" value="저장"/>
</form>
...

 
 
FileUploadsController(위 파일과 이름이 다름)

...

private void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    // 파일을 업로드할 디렉토리 경로 가져오기
    File uploadDirectory = FileUtil.getUploadDirectory();

    // 파일 업로드 후 파일 정보 반환
    List<FileVO> files = FileUtil.handleUploadFiles(request, uploadDirectory);

    // 파일 정보를 DB에 저장
    if (!files.isEmpty()) {
        FileDAO dao = new FileDAO();
        dao.insert(files);
    }

    response.sendRedirect("/uploads.do");
}

...

 
 
 

다운로드

download.jsp
편의상 1번 (File 테이블 id 필드의 값) 파일을 다운로드 한다고 가정해보자

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Download a file</title>
</head>
<body>
<form action="/download.do" method="post">
    <input type="hidden" name="seq" value="1">
    <input type="submit" value="파일 다운로드">
</form>
</body>
</html>

 
 
FileDownloadController

import com.cg.simplejsp._8_file.dao.FileDAO;
import com.cg.simplejsp._8_file.vo.FileVO;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

@WebServlet("/download.do")
public class FileDownloadController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        RequestDispatcher rd = request.getRequestDispatcher("file/download.jsp");
        rd.forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }

    private void processRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 파일 번호 받기
        long id = Long.parseLong(request.getParameter("seq"));

        // 파일 조회
        FileDAO dao = new FileDAO();
        FileVO vo = dao.findById(id);

        // 다운로드
        download(response, getFile(request, response, vo));
    }

    private static void download(HttpServletResponse response, File downloadFile) {
        try (FileInputStream fileInputStream = new FileInputStream(downloadFile);
             OutputStream outputStream = response.getOutputStream()
        ) {
        	// 파일을 읽어서 버퍼에 저장하고, 버퍼의 내용을 출력 스트림으로 전송
            fileInputStream.transferTo(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static File getFile(HttpServletRequest request, HttpServletResponse response, FileVO vo) {
        // 경로와 파일 이름
        String filePath = vo.getSavedPath();
        String fileName = vo.getSavedName();
        String fileOriginalName = vo.getOriginalName();

        // MIME 타입 가져오기
        String contentType = request.getServletContext().getMimeType(fileName);
        // MIME 타입이 없을 경우 기본값으로 설정
        if (contentType == null) {
            contentType = "application/octet-stream";
        }

        /*
        StandardCharsets.ISO_8859_1
        라틴 알파벳과 일부 특수 문자를 포함한 문자 집합
        파일 이름이 한글을 포함할 경우에 발생하는 인코딩 문제(한글 깨짐)를 해결하기 위해 설정
        */
        // 파일 이름을 UTF-8 형식으로 인코딩
        String encodingFileName = new String(fileOriginalName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);

        // HTTP 응답의 컨텐츠 타입과 헤더 설정
        response.setContentType(contentType);
        response.setHeader(
                "Content-Disposition",
                "attachment; filename=\"" + encodingFileName + "." + vo.getExt() + "\""
        );

        // 다운로드할 파일의 전체 경로 생성
        String fullPath = filePath + File.separator + fileName + "." + vo.getExt();

        return new File(fullPath);
    }
}

 
 

 
 
 

728x90

'JSP' 카테고리의 다른 글

[JSP]jakarta JSTL 설정  (0) 2024.06.02
IntelliJ JSP 시작 설정  (0) 2024.05.22
tomcat 설치, 실행  (0) 2024.05.21
이클립스 web.xml 생성  (0) 2023.06.18