반응형
servlet-context.xml
밑의 코드 추가
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<!-- 가상경로 -->
<resources mapping="/css/**" location="/resources/css/" />
<resources mapping="/javascript/**" location="/resources/js/" />
<resources mapping="/image/**" location="/resources/image/" />
<resources mapping="/upload/**" location="/resources/upload/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
<context:component-scan base-package="edu.springboard.controller" />
<!-- UserServiceImpl의 @Service -->
<context:component-scan base-package="edu.springboard.service" />
<context:component-scan base-package="edu.springboard.dao" />
</beans:beans>
web.xml
파일크기 지정을 위해 servlet 태그 안에 밑의 코드를 추가해준다
<multipart-config>
<max-file-size>104857600</max-file-size><!-- 10메가바이트 -->
<max-request-size>104857600</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- encoding -->
<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>
<!-- 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</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<max-file-size>104857600</max-file-size><!-- 10메가바이트 -->
<max-request-size>104857600</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
단순파일업로드
spring framework에서는 Multipart resolver 객체를 사용하여 파일을 업로드합니다
이때 업로드되는 위치는 우리가 알 수 없어 현재 업로드된 파일을 얻어와 지정 경로로 옮기게 됩니다
package edu.springboard.controller;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class UploadController {
@RequestMapping(value="/file/upload01.do", method = RequestMethod.GET)
public String upload01() {
return "upload/file01";
}
@RequestMapping(value="/file/upload01.do", method = RequestMethod.POST)
public String upload01(MultipartFile file01, HttpServletRequest request) throws IllegalStateException, IOException {
String path
= request.getSession().getServletContext().getRealPath("/resources/upload");
System.out.println("upload path:" + path);
File dir = new File(path);
//dir.exists() true이면 경로 있음
//dir.exists() false이면 경로 없음
if(!dir.exists()) { //경로가 없을시 해당 폴더 생성
dir.mkdirs(); //없는 상위폴더부터 전부 생성
}
//업로드된 파일이 있을 경우 위치 옮기기
if(!file01.getOriginalFilename().isEmpty()) {
UUID uuid = UUID.randomUUID();
String fileRealName = uuid.toString()+file01.getOriginalFilename();
file01.transferTo(new File(path,fileRealName));
}
return "redirect:/";
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일업로드</title>
<link rel="stylesheet" type="text/css" href="<%= request.getContextPath() %>/css/spring.css" />
<script src="<%= request.getContextPath() %>/javascript/jquery-3.7.1.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<script>
window.onload = function(){
// 파일이 선택될 때의 동작
$("#file").on('change', function(){
var fileName = $("#file").val().split('\\').pop(); // 경로 제거하고 파일명만 추출
if(fileName) {
$(".upload-name").val(fileName);
$("#preview").show();
// 파일 미리보기 처리 (이미지일 경우)
var file = this.files[0];
if (file && file.type.startsWith("image/")) {
var reader = new FileReader();
reader.onload = function(e) {
$('#preview').attr('src', e.target.result);
};
reader.readAsDataURL(file);
}
}
});
$(".user-container").on('click', function (e) {
// e.stopPropagation()을 사용하여 이벤트 중첩 방지
e.preventDefault(); // 기본 동작 중지
$("#file").trigger('click'); // 파일 입력 필드를 트리거
});
// 파일 입력 필드의 기본 클릭 동작 차단
$("#file").on('click', function (e) {
e.stopPropagation(); // 추가 이벤트 전파 차단
});
}
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
document.getElementById('preview').src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
} else {
document.getElementById('preview').src = "";
}
}
</script>
<style>
.user-container {
width: 300px;
padding: 10px;
box-sizing: border-box;
border: 1px solid #BFBFBF;
border-radius: 10px;
outline: none;
background-color: white;
font-weight: bold;
font-size: 18px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px; /* 요소 간 간격 */
position: relative; /* 내부 요소 배치 기준 */
}
.user-container i {
font-size: 20px; /* 아이콘 크기 */
color: #767676;
position: absolute; /* 절대 위치 지정 */
top: 50%; /* 세로 가운데 정렬 */
left: 10px; /* 아이콘 좌측 위치 */
transform: translateY(-50%); /* 세로 가운데 정렬 보정 */
pointer-events: none; /* 클릭 불가능하게 설정 */
}
.circular-img {
margin-left: 10px;
margin-bottom : 10px;
border-radius: 50%;
object-fit: cover;
display: block;
}
.user_inner .user-container {
display: flex;
align-items: center;
}
.profil {
display: flex;
align-items: center;
cursor: pointer;
width: 70%;
/* padding: 10px; */
border-radius:5px;
margin-right:20px;
}
.profil input[type="file"] {
position: absolute;
width: 0;
height: 0;
padding: 0;
overflow: hidden;
border: 0;
cursor: pointer;
}
.profil .upload-name {
display: inline-block;
height: 40px;
padding: 0 30px;
vertical-align: middle;
width: 100%;
color: #767676;
cursor: pointer;
border:none;
outline:none;
}
.profil label {
display: flex;
align-items: center;
height: 40px;
vertical-align: middle;
width: 100%;
color: #767676;
cursor: pointer;
}
</style>
</head>
<body>
<a href="<%= request.getContextPath() %>"><button class="sBtn">home</button></a>
<h2>단순파일업로드</h2><hr><!-- 파일데이터는 반드시 post 방식으로 넘겨야 함 -->
<form action="upload01.do" method="post" enctype="multipart/form-data">
<!-- file : <input type="file" name="file01"><br> -->
<div class="user_inner">
<div class="user-container">
<i class="fas fa-camera"></i>
<div class="profil">
<label for="file">
<input class="upload-name" value="파일업로드" placeholder="파일업로드" readonly>
<input type="file" id="file" name="file01" onchange="readURL(this);">
</label>
</div>
</div>
<img id="preview" class="circular-img"
style="max-width:150px; max-height:150px; border-radius:10px;" />
</div>
<button>업로드</button>
</form>
</body>
</html>
UUID.randomUUID()
- UUID(Universally Unique Identifier)를 생성하여 중복되지 않는 고유한 식별자를 생성합니다.
- 파일 이름 충돌을 방지하기 위한 안전한 방법입니다.
file01.getOriginalFilename()
- 업로드된 파일의 원래 이름(확장자 포함)을 가져옵니다. 이를 UUID에 결합해 원본 파일명을 유지하면서 고유성을 부여합니다.
결과
생성된 fileRealName은 UUID와 원래 파일명이 결합된 고유한 파일명이 됩니다.
UUID uuid = UUID.randomUUID();
String fileRealName = uuid.toString()+file01.getOriginalFilename();

다중파일업로드(각각 선택하여 여러개 업로드)
@RequestMapping(value="/file/upload02.do", method = RequestMethod.GET)
public String upload02() {
return "upload/file02";
}
@RequestMapping(value="/file/upload02.do", method = RequestMethod.POST)
public String upload02(MultipartFile file01,MultipartFile file02
,MultipartFile file03,HttpServletRequest request) throws IllegalStateException, IOException {
String path = request.getSession().getServletContext().getRealPath("/resources/upload");
System.out.println("upload path: " + path);
File dir = new File(path);
if(!dir.exists()) {
dir.mkdirs();
}
if(!file01.getOriginalFilename().isEmpty()) {
UUID uuid = UUID.randomUUID();
String fileRealName = uuid.toString()+file01.getOriginalFilename();
file01.transferTo(new File(path,fileRealName));
}
if(!file02.getOriginalFilename().isEmpty()) {
UUID uuid = UUID.randomUUID();
String fileRealName = uuid.toString()+file02.getOriginalFilename();
file02.transferTo(new File(path,fileRealName));
}
if(!file03.getOriginalFilename().isEmpty()) {
UUID uuid = UUID.randomUUID();
String fileRealName = uuid.toString()+file03.getOriginalFilename();
file03.transferTo(new File(path,fileRealName));
}
return "redirect:/";
}
<form action="upload02.do" method="post" enctype="multipart/form-data">
<div class="user-container">
<i class="fas fa-camera"></i>
<div class="profil">
<label for="file01">
<input class="upload-name" value="파일 업로드" placeholder="파일 업로드" readonly>
<input type="file" id="file01" name="file01" data-preview="preview01">
</label>
</div>
<img id="preview01" class="circular-img" style="display:none;" />
</div>
<div class="user-container">
<i class="fas fa-camera"></i>
<div class="profil">
<label for="file02">
<input class="upload-name" value="파일 업로드" placeholder="파일 업로드" readonly>
<input type="file" id="file02" name="file02" data-preview="preview02">
</label>
</div>
<img id="preview02" class="circular-img" style="display:none;" />
</div>
<div class="user-container">
<i class="fas fa-camera"></i>
<div class="profil">
<label for="file03">
<input class="upload-name" value="파일 업로드" placeholder="파일 업로드" readonly>
<input type="file" id="file03" name="file03" data-preview="preview03">
</label>
</div>
<img id="preview03" class="circular-img" style="display:none;" />
</div>
<button>업로드</button>
</form>

다중파일업로드(여러개 선택하여 업로드)
@RequestMapping(value="/file/upload03.do", method = RequestMethod.POST)
public String upload03(@RequestParam(value="multiFile")List<MultipartFile> multiFile,HttpServletRequest request) throws IllegalStateException, IOException {
String path = request.getSession().getServletContext().getRealPath("/resources/upload");
System.out.println("upload path: " + path);
File dir = new File(path);
if(!dir.exists()) {
dir.mkdirs();
}
for(MultipartFile file : multiFile) {
if(!file.getOriginalFilename().isEmpty()) {
UUID uuid = UUID.randomUUID();
String fileRealName = uuid.toString()+file.getOriginalFilename();
file.transferTo(new File(path,fileRealName));
}
}
return "redirect:/";
}
<form action="upload03.do" method="post" enctype="multipart/form-data">
<!-- ?multiFile=ddd&multiFile=dfee&multiFile=3333 -->
<!-- file : <input type="file" name="multiFile" multiple><br> -->
<div class="user-container">
<i class="fas fa-camera"></i>
<div class="profil">
<label for="multiFile">
<input class="upload-name" value="파일 업로드" placeholder="파일 업로드" readonly>
<input type="file" id="multiFile" name="multiFile" multiple data-preview="imagePreview">
</label>
</div>
</div>
<button>업로드</button>
</form>
<div id="imagePreview" class="slider-container">
</div>

절대경로 사용시 문제점
1. 개발도구를 사용하여 업로드를 진행시 업로드 위치를 워크스페이스의 프로젝트 절대경로로 잡게되면 이미지 등 업로드 파일이 실시간 반영되지 않는다. (복제하여 사용하는 가상의 위치까지 업로드 파일을 복사하는 시간차 때문에)
2.개발서버의 업로드 위치와 운영서버의 업로드 위치가 다르게 되면 배포마다 경로를 수정하여 반영해야하는 문제가 발생한다.
상대경로 사용시 문제점
개발서버 작업시 개발도구가 복제하여 사용하는 프로젝트 위치로 바로 업로드 되기 때문에 실제 워크스페이스의 업로드 위치로는 파일이 저장되지 않는다 (별도로 파일을 복사하여 워크스페이스에 추가하는 과정이 발생한다)
반응형
'Spring' 카테고리의 다른 글
[Spring] 필터(filter) (0) | 2024.12.03 |
---|---|
[Spring] 인터셉터(interceptor) (0) | 2024.12.02 |
[Spring] AJAX 여러건의 데이터 받기 (0) | 2024.11.30 |
[Spring] AJAX 한 건의 데이터 받기 (1) | 2024.11.29 |
[Spring] MVC 연습 (0) | 2024.11.28 |