다중 파일 업로드를 위해 HTTP 에서 제공하는 multipart/form-data 전송 방식을 사용하면
form 태그에 담긴 각각 다른 형식의 데이터들을 구분해주므로
key / value 형태의 문자 데이터와 바이너리 형태의 파일 데이터를 함께 전송할 수 있다.
이렇게 전송한 데이터는 Spring 의 MultipartFille 인터페이스를 통해 받을 수 있으며
이러한 기능을 구현하기 위해서 우선 몇 가지 환경 설정이 필요하다.
• 환경 설정 및 Form 태그 구성
SpringBoot / Maven 기준, 파일 업로드와 io 관련 디펜던시를 추가한다.
<!-- file upload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
application.properties 파일에 아래의 내용을 추가한다.
# file upload
spring.servlet.multipart.enabled:true
spring.servlet.multipart.max-request-size:10MB
spring.servlet.multipart.max-file-size:20MB
max-request-size 은 request 요청의 크기 제한을 설정, max-file-size 은 업로드할 파일의 크기 제한을 설정한다.
jsp 페이지에 작성할 간략한 form 태그 양식은 아래와 같다.
<form enctype="multipart/form-data" action="upload" method="post">
<input type="text" name="text">
<input type="file" name="uploadFiles" multiple>
<button type="submit">Submit</button>
</form>
form 태그에 enctype="multipart/form-data" 속성을 추가한다.
다중 파일을 전송할 input 태그엔 둘 이상의 값을 입력할 수 있음을 명시하는 multiple 속성을 추가하며
각 태그마다 controller 에서 해당 태그를 인식할 수 있는 name 값을 명시한다.
• Controller 구성
public String upload(MultipartHttpServletRequest mtr, HttpServletRequest request, Model model, MultiDTO multi)
throws Exception {
List<MultipartFile> fileList = mtr.getFiles("uploadFiles");
String path = request.getSession().getServletContext().getRealPath("upload");
MultipartHttpServletRequest 객체를 통해 전송받은 다중 파일들을 제네릭이 MultipartFile 인 List 에 담는다.
HttpServletRequest 객체로 파일이 저장될 미리 생성해 놓은 upload 폴더의 경로를 가져와 변수에 담는다.
StringBuilder files = new StringBuilder();
for (MultipartFile mf : fileList) {
String originalFilename = mf.getOriginalFilename();
StringTokenizer st = new StringTokenizer(originalFilename, ".");
String[] file = new String[2];
file[0] = st.nextToken();
file[1] = st.nextToken();
db에 저장될 다중 파일명을 만들기 위해 StringBuilder 객체를 생성한다.
파일들을 foreach 문으로 하나씩 가져와 변수에 저장하고
"." 을 기준으로 파일명과 확장자로 분리하여 배열에 저장한다.
int flag = 0;
int size = (int) mf.getSize();
if (size > 10000000) {
model.addAttribute("flag", flag);
return "alert/upload_alert";
} else if (!file[1].equals("jpg") && !file[1].equals("gif") && !file[1].equals("jpeg")
&& !file[1].equals("png")) {
flag = 1;
model.addAttribute("flag", flag);
return "alert/upload_alert";
}
파일 크기가 지정한 크기를 초과할 경우, 또는 지원하지 않는 확장자일 경우 각각 분기를 주며
아래의 내용과 같은 alert 창을 띄워 파일 업로드를 취소시킨다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:if test="${flag == 0 }">
<script type="text/javascript">
alert("Files can be uploaded up to 10MB.");
history.go(-1);
</script>
</c:if>
<c:if test="${flag == 1 }">
<script type="text/javascript">
alert("Only jpg, gif, png, and jpeg files can be uploaded.");
history.go(-1);
</script>
</c:if>
넘겨받은 flag 변수의 값에 따라 jstl if 문을 통해 분기를 주어 alert 창을 띄우고 원래 페이지로 돌아간다.
String randomFilename = System.currentTimeMillis() + originalFilename;
files.append(randomFilename + ",");
mf.transferTo(new File(path + "/" + randomFilename));
저장된 파일이 여러 개이므로 System.currentTimeMillis() 로 파일명의 중복을 방지한다.
또는 UUID.randomUUID().toString() 으로 랜덤 파일명을 생성할 수 도 있다.
이전에 생성한 StringBuilder 객체에 새로 생성된 파일명과 "," 를 더해주고,
MultipartFile 객체를 통해 미리 변수에 저장한 경로에 해당 파일을 저장한다.
multi.setPhoto(files.toString());
service.insert(multi);
새로 생성된 다중 파일명을 dto에 따로 저장하고
해당 dto 를 파라미터로 전달해 db에 insert 한다.
예시로 파일이 3개이고 파일명이 Filename1, Filename2, Filename3 일 때
"1656009274865Filename.jpg,1656009274867Filename.jpg,1656009274868Filename.jpg," 로 저장된다.
전체 controller 코드는 아래와 같다.
@RequestMapping("upload")
public String upload(MultipartHttpServletRequest mtr, HttpServletRequest request, Model model, MultiDTO multi)
throws Exception {
List<MultipartFile> fileList = mtr.getFiles("uploadFiles");
String path = request.getSession().getServletContext().getRealPath("upload");
StringBuilder files = new StringBuilder();
for (MultipartFile mf : fileList) {
String originalFilename = mf.getOriginalFilename();
StringTokenizer st = new StringTokenizer(originalFilename, ".");
String[] file= new String[2];
file[0] = st.nextToken();
file[1] = st.nextToken();
int flag = 0;
int size = (int) mf.getSize();
if (size > 10000000) {
model.addAttribute("flag", flag);
return "alert/upload_alert";
} else if (!file[1].equals("jpg") && !file[1].equals("gif") && !file[1].equals("jpeg")
&& !file[1].equals("png")) {
flag = 1;
model.addAttribute("flag", flag);
return "alert/upload_alert";
}
String randomFilename = System.currentTimeMillis() + originalFilename;
files.append(randomFilename + ",");
mf.transferTo(new File(path + "/" + randomFilename));
}
multi.setPhoto(files.toString());
service.insert(multi);
return "redirect:/list";
}
다중 이미지를 view 단에 출력 시에는 forTokens jstl 태그를 활용한다.
<c:forTokens var="image" items="${photo}" delims="," begin="0" end="4">
<div><img src="upload/${image}"/></div>
</c:forTokens>
지정한 var 변수에 "," 을 기준으로 items 에서 파일을 하나씩 가져오고, 0 ~ 4 의 범위만큼 출력한다.
'Web Development > Spring' 카테고리의 다른 글
[Spring] WebClient - 2. 요청 핸들링 및 API 구성 (0) | 2025.02.26 |
---|---|
[Spring] WebClient - 1. 개념 및 기본 설정 (0) | 2025.02.26 |
[Spring] FlashMap 을 활용하여 Controller 간 값 전달 (0) | 2022.06.29 |
댓글