[Spring-Framework] 12. 파일 업로드 구현하기, C태그 사용(if, else, foreach)
Spring-Framework로 파일 업로드 구현하는 방법에 대해서 소개하겠다.
[작업 환경]
* 웹 서버: Apache-tomcat-9.0.37-windows-x64
* IDE: Spring-Tool-Suite 4-4.7.2. Releases.
* Eclipse Marketplace: STS 검색 후 Spring Tools 3 Add-On for Spring Tools 4 3.9.14.Release 설치할 것
* POM 라이브러리:
- Apache Commons IO 2.8.0 (2020. 09)
- Apache Commons FileUpload (2018. 12)
* C태그(JSTL)
1. Eclipse Marketplace 설치하기
help -> eclipse marketplace를 클릭한다.
그림 1. Spring Tools 3 Add-On for Spring Tools 4 3.9.14. Releases 설치하기
2. pom.xml 설정하기
http://mvnrepository.com 사이트에 접속한다.
그림 2. MVN-Repository (검색어: common-io)
검색은 "common-io"를 검색한다.
그림 3. Apache Commons IO - 2.8.0
최신 2.8.0을 클릭하여 pom을 복사한다. (maven)
그림 4. MVN-Repository (검색어: commons-fileupload)
검색은 "commons-fileupload"를 검색한다.
그림 5. Apache Commons FileUpload (1.4)
최신 1.4를 클릭하여 pom을 복사한다. (maven)
그림 6. MVN-Repository (검색어: servlet)
검색은 "Java Servlet API"를 검색한다.
그림 7. Java Servlet API (2018. 4)
최신 4.0.1을 클릭하여 pom을 복사한다. (maven)
그림 8. pom.xml 파일 수정하기
javax.servlet은 버전을 바꿔준다.
commons-io, commons-fileupload는 추가해준다.
(중략)
<!-- Servlet -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
(중략)
* 파일명: pom.xml
3. web.xml - Servlet 2.5 스팩 -> 3.1 스팩으로 변경하기
스팩 정보에 관한 것이다.
Spring Legacy Project의 Spring MVC Project를 생성하면, servlet 2.5버전의 web.xml가 생성되는 것을 확인할 수 있다.
Spec 2.5에서 Spec 3.1로 변경해줘야 한다.
그림 9. web.xml (servlet 2.5 -> servlet 3.1 스팩으로 변경 전)
그림 10. web.xml (servlet 2.5 -> servlet 3.1 스팩으로 변경 후)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
(중략)
* 파일명: web.xml (servlet 3.1 스팩)
4. context-common.xml - 생성하기
context-common.xml을 하나 만들어주겠다. 만드는 이유는 파일 업로드 관련해서 만드는 것이다.
경로: /src/main/webapp/WEB-INF/spring/appServlet/
그림 11. appServlet 폴더에서 마우스 오른쪽 버튼 모습
"appServlet 폴더에서 오른쪽 버튼" -> "New" -> "File"을 클릭한다.
그림 12. context-common.xml 파일 만들기
file명은 context-common.xml이라고 입력한 후 Finish를 누른다.
그림 13. context-common.xml 파일 수정하기
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- MultipartResolver 설정 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000000" />
<property name="maxInMemorySize" value="100000000" />
</bean>
</beans>
* 파일명: context-common.xml
[첨부(Attachments)]
context-common.zip
[비고]
파일 크기의 제한을 두기 위한 코드이다.
예: 최대 업로드 파일 크기는 20MB, 메모리에 최대로 저장할 수 있는 파일 크기는 10MB로 제한함.
1MB = 1,024KB
10MB = 10,485,760KB(1,024*1,024*10)
20MB = 20,971,520KB(1,024*1,024*20)
5. web.xml - context-common.xml 파일 인식시켜주기
web.xml을 연다.
그림 14. web.xml 수정 모습
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/appServlet/servlet-context.xml
/WEB-INF/spring/appServlet/context-common.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- UTF-8 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param> <init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
파일명: web.xml
[첨부(Attachments)]
web.zip
6. (직접 파일구현에 있어서 연관이 있는지?): 아니다.
- MemberVO.java
* 패키지 경로: com.example.postget.vo
package com.example.postget.vo;
public class MemberVO {
private int num;
private String id;
private String passwd;
private String address;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
파일명: MemberVO.java
[첨부(Attachments)]
MemberVO.zip
7. Controller와 View 영역
지금 작업부터는 사용자 관점에서 파일 업로드를 구현하면 된다.
어떠한 작업을 할 것인지 개략적인 프로세스로 살펴보겠다.
그림 15. 프로세스 정의 - 파일 업로드
[첨부(Attachments)]
process.zip
process.pptx
8. Controller - BoardController.java 설정하기
POST 방식으로 /board/uploadResult를 호출하면, 파일 업로드 프로세스를 처리해주는 과정이 담긴 코드이다.
물론 루트 경로 등은 따로 별도로 재정의를 해서 사용하면 좋을 듯 싶다.
여력이 되면, 수정해봐도 좋을 듯 하다.
* 패키지 경로: com.example.postget.controller
그림 16, 그림 17. BoardController의 업로드 핵심 내용
POST 방식으로 업로드 처리에 관한 것이다.
(중략)
@RequestMapping(value = "uploadResult", method = RequestMethod.POST)
public String uploadResult(Model model, MemberVO memberVO, MultipartHttpServletRequest request) {
// 루트 경로
String rootUploadDir = "C:"+File.separator+"Upload"; // C:/Upload
File dir = new File(rootUploadDir + File.separator + "testfile");
if(!dir.exists()) { //업로드 디렉토리가 존재하지 않으면 생성
dir.mkdirs();
}
Iterator<String> iterator = request.getFileNames(); // 다중 업로드 지원
ArrayList<String> list = new ArrayList<String>();
int fileLoop = 0;
String uploadFileName;
MultipartFile mFile = null;
String orgFileName = ""; // 진짜 파일명
String sysFileName = ""; // 변환된 파일명
list.add("루트경로:" + dir + File.separator );
while(iterator.hasNext()) {
fileLoop++;
uploadFileName = iterator.next();
mFile = request.getFile(uploadFileName);
orgFileName = mFile.getOriginalFilename();
logger.info(orgFileName);
if(orgFileName != null && orgFileName.length() != 0) { //sysFileName 생성
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMDDHHmmss-" + fileLoop);
Calendar calendar = Calendar.getInstance();
sysFileName = simpleDateFormat.format(calendar.getTime()); //sysFileName: 날짜-fileLoop번호
try {
logger.info("try 진입");
mFile.transferTo(new File(dir + File.separator + sysFileName)); // C:/Upload/testfile/sysFileName
list.add("원본파일명: " + orgFileName + ", 시스템파일명: " + sysFileName);
list.add("파일크기: " + mFile.getSize() + ", 파일형식: " + mFile.getContentType() );
}catch(Exception e){
list.add("오류: 파일 업로드 중 에러발생!!!");
}
}//if
}//while
model.addAttribute("list", list);
return "/board/uploadResult";
}
(중략)
파일명: BoardController.java
[첨부(Attachments)]
BoardController.zip
9. View - board/insert.jsp
사용자 화면에 관한 것이다.
그림 18. /board/insert 페이지
미리 살펴보는 /board/insert 페이지에 대한 것이다.
파일 업로드 영역에 관한 핵심은 아래의 그림과 같다.
그림 19. views/board/insert.jsp
<!-- 파일 업로드 영역 -->
<h3>파일 업로드</h3>
<form method="POST" action="uploadResult" enctype="multipart/form-data">
<table style="width:900px; height:70px;">
<tr>
<td style="width:10%">아이디(UserID)</td>
<td style="width:90%">
<input type="text" name="id" class="login_id">
</td>
</tr>
<tr>
<td>
비밀번호(Password)
</td>
<td>
<input type="password" name="passwd" class="login_pwd">
</td>
</tr>
<tr>
<td colspan="2">
<input type="file" name="uploadFile" multiple>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="로그인">
</td>
</tr>
</table>
</form>
파일명: insert.jsp
[첨부(Attachments)]
insert.zip
10. View - board/uploadResult.jsp
uploadResult 페이지에 대한 것이다.
앞서 Controller에서 정의된 것을 출력하는 페이지를 가리킨다.
그림 20. /board/uploadResult 페이지
소스코드는 다음과 같다.
c태그를 간단하게 if, else문도 적용시켜보았으니 참고해보면, 좋을 듯 싶다.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>업로드 - 결과</title>
</head>
<body>
<!-- 업로드 결과 (출력) -->
<c:set var="i" value="1" />
<c:set var="num" value="1" />
<table style="width:700px; border:1px;">
<c:forEach items="${list}" var="list">
<c:choose>
<c:when test="${i == 1}">
<tr>
<td colspan="2" style="background-color:#e2e2e2;">
<c:out value="${num}" />
</td>
</tr>
</c:when>
</c:choose>
<c:choose>
<c:when test="${i <= 3}">
<tr>
<td><c:out value="${i}" /></td>
<td>${list}</td>
<c:set var="i" value="${i + 1}" />
</tr>
</c:when>
<c:otherwise>
<c:set var="i" value="1" />
<c:set var="num" value="${num + 1}" />
</c:otherwise>
</c:choose>
</c:forEach>
</table>
</body>
</html>
* 파일명: uploadResult.jsp
[첨부(Attachments)]
uploadResult.zip
11. 파일 업로드 결과 - 실제 업로드된 모습
그림 21. 파일 업로드 모습 (2020-09-24)
12. 맺음글(Conclusion)
파일 업로드 방법에 대해서 살펴보았다.
* 참고자료(References)
1. Maven Repository: commons-fileupload » commons-fileupload » 1.4, https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload/1.4, Accessed by 2020-09-24, Last Modified 2018-12-24.
2. Maven Repository: commons-io » commons-io » 2.8.0, https://mvnrepository.com/artifact/commons-io/commons-io/2.8.0, Accessed by 2020-09-24, Last Modified 2020-09-09.
3. web.xml 서블릿 버전별 DTD, https://antop.tistory.com/entry/webxml-%EC%84%9C%EB%B8%94%EB%A6%BF-%EB%B2%84%EC%A0%84%EB%B3%84-DTD#footnote_link_145_1, Accessed by 2020-09-24, Last Modified 2013-03-03.