파일 업로드
1. 등록 폼에 파일 업로드 필드 추가
- 컨트롤러를 MultipartFile 객체를 이용해서 업로드 가능한 컨트롤로 구현해야 한다.
- 업로드된 파일은 대부분 바이너리 파일이다
- 파일이 첨부된 폼을 전송하는 경우 콘텐츠형식은 multipart/form-data를 해야 한다
- 폼이 전송되면 이미지 파일의 바이너리 데이터를 포함하는 한 부분이 멀티파트 형식으로 전달된다.
2. Controller의 업로드된 파일을 받도록 수정
3. 스프링에 멀티파트 파일 리졸버(multipart file resolver) 설정
- 실제로 파일 업로드 기능이 동작하기 위해서는 반드시 사용자가 업로드한 파일 정보가
MultipartFile 객체에 설정되어야 하며, 이를 위해서 멀티파트 리졸버 객체가 반드시 필요하다.
4. 다중 파일 업로드 시에는 <input type="file" name="">이 2개 이상 있을 때는 name속성에
같은 이름을 지정해야한다
* 파일 업로드를 위한 스프링 설정
DispatcherServlet은 컨트롤러에 해당 데이터를 제공하기 위하여 POST 요청에서 멀티파트 데이터를 추출하는 멀티파트 리졸버가 필요하다
스프링이 CommonsMultipartResolver만 제공한다.
파일 업로드가 정상적으로 동작하기 위해서는 내가 만든 컨트롤러뿐만 아니라 이를 위해 멀티파트 리졸버 객체를 메모리에 올리는 두 개의 객체 생성 과정이 필요한 것이다.
pom.xml
commons-io
<!-- 파일 업로드 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
index.jsp
<p><a href="/spring2/user/uploadForm">파일 업로드</a></p>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>*** 메인화면 ***</h2>
<h3>
<p><a href="/spring2/user/writeForm">입력</a></p>
<p><a href="/spring2/user/list?pg=1">출력</a></p> <!-- pg=1 일 때는 생략 가능 -->
<br>
<p><a href="/spring2/user/uploadForm">파일 업로드</a></p>
</h3>
</body>
</html>
user.service에 필요없는 것들 좀 지우자 !!!
user.controller
UserController.java
UserUploadController.java
package user.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(value="user")
public class UserUploadController {
@RequestMapping(value = "uploadForm")
public String uploadForm() {
return "/upload/uploadForm";
}
}
uploadForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
table {
border-collapse: collapse;
}
th, td {
padding: 5px;
}
</style>
</head>
<body>
<form method="post" enctype="multipart/form-data" action="/spring2/user/upload">
<table border="1">
<tr>
<th>상품명</th>
<td>
<input type="text" name="imageName" size="35">
</td>
</tr>
<tr>
<td colspan="2">
<textarea name="imageContent" rows="10" cols="50"></textarea>
</td>
</tr>
<tr>
<td colspan="2">
<input type="file" name="img">
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="이미지업로드">
<input type="reset" value="취소">
</td>
</tr>
</table>
</form>
</body>
</html>
Folder: storage (가상주소, 업로드) 만들기
user.bean
UserUploadDTO.java
imageName
imageContent
imageFileName
imageOriginalFileName
package user.bean;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UserUploadDTO {
private int seq;
private String imageName; //상품명
private String imageContent; //상품 내용
private String imageFileName; //UUID에서 얻은 이름
private String imageOriginalFileName; //이미지의 원래 이름
}
UUID 란?
UUID(Universally Unique IDentifier)는 네트워크상에서 고유성(중복 안되게)을 보장하는
ID를 만들기 위한 표준 규약이다.
UUID.randomUUID()는 무작위의 UUID를 생성하는 Java의 정적 메서드이고,
항상 고유한 값으로 UUID를 생성한다.
UUID는 다음과 같이 32개의 16진수로 구성되며 5개의 그룹으로 표시되고 각 그룹은 붙임표(-)로 구분한다.
280a8a4d-a27f-4d01-b031-2a003cc4c039
적어도 서기 3400년까지는 같은 UUID가 생성될 수 없다고 한다.
이러한 이유로 UUID를 데이터베이스의 프라이머리 키(primary key)로 종종 사용한다.
위에서 UUID를 이용하여 이미지 등록할 때 홈런볼.jpg을 업로드 했더니 storage 폴더에
447e3573-01ca-4a68-be6a-ddce14bc07c8 으로 등록 된 것을 확인 할 수 있다.
sql
userUpload.sql
#Oracle
create table userUpload (
seq int(10) primary key,
imageName varchar2(50),
imageContent varchar2(4000),
imageFileName varchar2(100) not null,
imageOriginalFileName varchar2(100) not null);
create sequence SEQ_USERIMAGE nocycle nocache;
#MySQL
create table userUpload (
seq int(10) primary key auto_increment,
imageName varchar(50),
imageContent varchar(4000),
imageFileName varchar(100) not null,
imageOriginalFileName varchar(100) not null);
UserUploadController.java
@RequestMapping(value = "upload", method = RequestMethod.POST)
public String upload(@ModelAttribute UserUploadDTO userUploadDTO,
@RequestParam MultipartFile img) {
return "/upload/uploadForm";
}
servlet-context.xml
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<beans:property name="maxUploadSize" value="5000000"/> <!-- 5MB -->
</beans:bean>
UserUploadController.java
//실제폴더
String filePath = 세션 객체
HttpSession session = request.getSession();
원래는 이렇게했지만 upload( ) 이건 콜백함수라서 스프링에서 세션을 제공해준다.
@RequestMapping(value = "upload", method = RequestMethod.POST)
public String upload(@ModelAttribute UserUploadDTO userUploadDTO,
@RequestParam MultipartFile img,
HttpSession session) {
//실제폴더
String filePath = session.getServletContext().getRealPath("WEB-INF/storage");
System.out.println("실제 폴더 = " + filePath);
return "";
}
실행결과
실제 폴더 = D:\Spring\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\
SpringProject2\WEB-INF\storage
UserUploadController.java
String imageOriginalFileName = img.getOriginalFilename();
File file = new File(filePath, imageOriginalFileName);
try {
img.transferTo(file);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "";
@RequestMapping(value = "upload", method = RequestMethod.POST)
public String upload(@ModelAttribute UserUploadDTO userUploadDTO,
@RequestParam MultipartFile img,
HttpSession session) {
//실제폴더
String filePath = session.getServletContext().getRealPath("WEB-INF/storage");
System.out.println("실제 폴더 = " + filePath);
String imageOriginalFileName = img.getOriginalFilename();
File file = new File(filePath, imageOriginalFileName);
try {
img.transferTo(file);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
D:\Spring\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\
SpringProject2\WEB-INF\storage
이 경로로 들어가면 이미지가 업로드된 것을 확인할 수 있다.
UserUploadController.java
String result = "<span>"
+ "<img src='/spring2/storage/" + imageOriginalFileName + "' width='300' height='300'"
+ "</span>";
String result = "<span>"
+ "<img src='/spring2/storage/" + imageOriginalFileName + "' width='300' height='300'"
+ "</span>";
return result;
servlet-context.xml
<resources location="/WEB-INF/storage/" mapping="/storage/**"></resources>
System.out.println(userUploadDTO.getImageName() + ","
+ userUploadDTO.getImageContent() + ","
+ userUploadDTO.getImageFileName() + ","
+ userUploadDTO.getImageOriginalFileName());
userUploadDTO.setImageOriginalFileName(imageOriginalFileName);
지금까지 한 건 데이터가 1개만 넘어올 때이다.
이제는 데이터가 2개 이상 들어올 때를 해볼 것이다.
파일이 2개 이상일 때
uploadForm.jsp
<!-- 다중 업로드할 때는 name 속성의 이름이 같아야한다. 서버에서 배열로 받기 때문에 !! -->
<tr>
<td colspan="2">
<input type="file" name="img">
</td>
</tr>
<tr>
<td colspan="2">
<input type="file" name="img">
</td>
</tr>
UserUploadController.java
@RequestParam MultipartFile[] img,
String imageOriginalFileName;
File file;
String result;
//----------------------------------
imageOriginalFileName = img[0].getOriginalFilename();
file = new File(filePath, imageOriginalFileName);
try {
img[0].transferTo(file);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
result = "<span>"
+ "<img src='/spring2/storage/" + imageOriginalFileName + "' width='300' height='300'"
+ "</span>";
//----------------------------------
//데이터가 2개 이상이 넘어올 때
@ResponseBody
@RequestMapping(value = "upload", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
public String upload(@ModelAttribute UserUploadDTO userUploadDTO,
@RequestParam MultipartFile[] img,
HttpSession session) {
//실제폴더
String filePath = session.getServletContext().getRealPath("WEB-INF/storage");
System.out.println("실제 폴더 = " + filePath);
String imageOriginalFileName;
File file;
String result;
//----------------------------------
imageOriginalFileName = img[0].getOriginalFilename();
file = new File(filePath, imageOriginalFileName);
try {
img[0].transferTo(file);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
result = "<span>"
+ "<img src='/spring2/storage/" + imageOriginalFileName + "' width='300' height='300'"
+ "</span>";
//----------------------------------
imageOriginalFileName = img[1].getOriginalFilename();
file = new File(filePath, imageOriginalFileName);
try {
img[1].transferTo(file);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
result += "<span>"
+ "<img src='/spring2/storage/" + imageOriginalFileName + "' width='300' height='300'"
+ "</span>";
//DB
return result;
}
uploadForm.jsp -- 주석걸기
<%--
<!-- 다중 업로드할 때는 name 속성의 이름이 같아야한다. 서버가 배열로 받기 때문에 !! -->
<tr>
<td colspan="2">
<input type="file" name="img">
</td>
</tr>
<tr>
<td colspan="2">
<input type="file" name="img">
</td>
</tr>
--%>
한 번에 1개 또는 여러개(드래그)를 선택
드래그해서 한 번에 여러개가 올라가게 하겠다 !!
여기서는 배열로 받는게 아닌 List로 받는다 !!
<!-- 한 번에 1개 또는 여러개(드래그)를 선택 => 서버에서 List로 받기 때문에 !!-->
<tr>
<td colspan="2">
<input type="file" name="img[]" multiple="multiple">
</td>
</tr>
UserUploadController.java
//1개 또는 여러개(드래그해서 넘어오는 경우)
@ResponseBody
@RequestMapping(value = "upload", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
public String upload(@ModelAttribute UserUploadDTO userUploadDTO,
@RequestParam("img[]") List<MultipartFile> list,
HttpSession session) {
//실제폴더
String filePath = session.getServletContext().getRealPath("WEB-INF/storage");
System.out.println("실제 폴더 = " + filePath);
String imageOriginalFileName;
File file;
String result = "";
//파일들을 모아서 DB로 보내기
List<UserUploadDTO> imageUploadList = new ArrayList<>();
for(MultipartFile img: list) {
imageOriginalFileName = img.getOriginalFilename();
file = new File(filePath, imageOriginalFileName);
try {
img.transferTo(file);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
result += "<span>"
+ "<img src='/spring2/storage/"
+ URLEncoder.encode(imageOriginalFileName, "UTF-8")
+ "' width='300' height='300'"
+ "</span>";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
UserUploadDTO dto = new UserUploadDTO();
dto.setImageName(userUploadDTO.getImageName());
dto.setImageContent(userUploadDTO.getImageContent());
dto.setImageFileName("");
dto.setImageOriginalFileName(imageOriginalFileName);
imageUploadList.add(dto);
}//for
//DB
return result;
}
파일명에 한글 또는 공백이 있어도 업로드가 된다.
@RequestMapping(value = "upload", method = RequestMethod.POST, produces = "text/html; charset=UTF-8")
이 방법은 파일명에 공백이 있으면 안된다.
URLEncoder.encode(imageOriginalFileName, "UTF-8")
파일 업로드 AJAX
index.jsp
<h3><p><a href="/spring2/user/uploadAJAXForm">파일 업로드 AJAX</a></p></h3>
<body>
<h2>*** 메인화면 ***</h2>
<h3>
<div class="nav-bar">
<h3><p><a href="/spring2/user/writeForm">입력</a></p></h3>
<h3><p><a href="/spring2/user/list?pg=1">출력</a></p></h3> <!-- pg=1 일 때는 생략 가능 -->
<h3><p><a href="/spring2/user/uploadForm">파일 업로드</a></p></h3>
<h3><p><a href="/spring2/user/uploadAJAXForm">파일 업로드 AJAX</a></p></h3>
</div>
</h3>
</body>
UserUploadController.java
@RequestMapping(value = "uploadAJAXForm")
public String uploadAJAXForm() {
return "/upload/uploadAJAXForm";
}
uploadAJAXForm.jsp
<tr>
<td colspan="2">
<span id="showImageList">이미지 미리보기</span>
<img alt="카메라" src="../image/camera.png">
<input type="file" name="img[]" multiple="multiple">
</td>
</tr>
<%@ 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" href="../css/uploadForm.css">
</head>
<body>
<div id="container">
<div id="header">
<a href="/spring2/"><img src="../image/free-icon-love-4096198.png" alt="홈" width="60" height="60"/></a>
</div>
<form id="uploadAJaxForm">
<table>
<tr>
<th>상품명</th>
<td>
<input type="text" name="imageName" size="35">
</td>
</tr>
<tr>
<th>상품내용</th>
<td colspan="2">
<textarea name="imageContent" rows="10" cols="50"></textarea>
</td>
</tr>
<!-- 한 번에 1개 또는 여러개(드래그)를 선택 => 서버에서 List로 받기 때문에 !!-->
<tr>
<td colspan="2">
<span id="showImageList">이미지 미리보기</span>
<img alt="카메라" src="../image/camera.png">
<input type="file" name="img[]" multiple="multiple">
</td>
</tr>
<tr>
<td colspan="2" align="center" style="text-align: center;">
<input type="button" value="이미지 업로드">
<input type="reset" value="취소">
</td>
</tr>
</table>
</form>
</div>
<script type="text/javascript" src="http://code.jquery.com/jquery-3.7.1.min.js"></script>
<script type="text/javascript">
$('#camera').click(function(){
$('#img').trigger('click'); //강제 이벤트 발생
});
</script>
</body>
</html>
카메라 눌렀을 때 파일등록이랑 같은 역할 !!
FileReader 란?
FileReader는 type이 file인 input 태그 또는 API 요청과 같은 인터페이스를 통해
File 또는 Blob 객체를 편리하게 처리할수있는 방법을 제공하는 객체이며
abort, load, error와 같은 이벤트에서 발생한 프로세스를 처리하는데 주로 사용되며,
File 또는 Blob 객체를 읽어서 result 속성에 저장한다.
FileReader도 비동기로 동작한다.
FileReader.onload()
load 이벤트의 핸들러. 이 이벤트는 읽기 동작이 성공적으로 완료되었을 때마다 발생한다.
//이미지 미리보기
$('#img').change(function(){
$('#showImageList').empty();
for(var i=0; i<this.files.length; i++){
readURL(this.files[i]);
}
});
function readURL(file){
var reader = new FileReader();
var show;
reader.onload = function(e){
var img = document.createElement('img');
img.src = e.target.result;
img.width = 70;
img.height = 70;
$('#showImageList').append(img);
}
reader.readAsDataURL(file);
}
이미지 업로드하러 가자 !!
<input type="button" value="이미지 업로드" id="uploadAJaxBtn"> -- 버튼 id 추가
<tr>
<td colspan="2" align="center" style="text-align: center;">
<input type="button" value="이미지 업로드" id="uploadAJaxBtn">
<input type="reset" value="취소">
</td>
</tr>
<script type="text/javascript" src="../js/uploadAJax.js"></script>
processData
- 기본값은 true
- 기본적으로 Query String으로 변환해서 보내진다('변수=값&변수=값')
- 파일 전송시에는 반드시 false로 해야 한다.(formData를 문자열로 변환하지 않는다)
contentType
- 기본적으로 "application/x-www-form-urlencoded; charset=UTF-8"
- 파일 전송시에는 'multipart/form-data'로 전송이 될 수 있도록 false로 설정한다
uploadAJax.js
$(function(){
$('#uploadAJaxBtn').click(function(){
let formData = new FormData($('#uploadAJaxForm')[0]);
$.ajax({
type: 'post',
enctype: 'multipart/form-data',
processData: false,
contentType: false,
url: '/spring/user/upload',
data: formData,
success: function(data){
alert(data);
},
error: function(e){
console.log(e);
}
});//$.ajax
});
});
'Spring' 카테고리의 다른 글
DAY 68 - 스프링 프레임워크 - NCP 파일 업로드 / 수정 (2024.10.14) (4) | 2024.10.15 |
---|---|
DAY 67 - 스프링 프레임워크 HOMEWORK - 파일 DB 저장 / 이미지 출력 (2024.10.11) (0) | 2024.10.14 |
DAY 66 - 스프링 프레임워크 MVC HOMEWORK - 회원탈퇴 (2024.10.10) (1) | 2024.10.11 |
DAY 66 - 스프링 프레임워크 MVC- 회원가입 / 회원목록 / 회원정보수정 (2024.10.10) (1) | 2024.10.10 |
DAY 65 - 스프링 프레임워크 - MVC (2024.10.08) (0) | 2024.10.08 |