Spring MVC02 - 다중파일 업로드
1. 파일 업로드를 위한 준비 API, servlet-context.xml설정
다중 파일 업로드를 위해 API를 다운받자.
commons-fileupload, commons-io
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
스프링 컨테이너에 servlet-context.xml 파일 업로드를 위한 클래스와 property를 설정해야한다.
<beans:bean id="multipartResolver" class ="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<beans:property name="maxUpLoadSize" value="52428800"></beans:property>
<beans:property name="maxInMemorySize" value="1000000"></beans:property>
<beans:property name="defaultEncoding" value="utf-8"></beans:property>
</beans:bean>
- 업로드할 파일의 최대사이즈와, 임시파일사이즈, 인코딩타입 등을 미리 정해두자
1.2 뷰 페이지 만들기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css'>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js'></script>
<script type="text/javascript">
var cnt=1;
function delUploadDiv(cnt){
$("#f"+cnt+"").remove();
$("#c"+cnt+"").remove();
cnt--;
}
function file_add(){
//파일 추가 버튼 누를때 마다 file 넣을 수 있는 칸 생김
$("#d_file").append("<input type='file' id='f"+cnt+"' name='file"+cnt+"'/>"+"<input type='button' id='c"+cnt+"' value='삭제' onclick = 'delUploadDiv("+cnt+")'>"+"<br>");
cnt++;
}
</script>
<title>Insert title here</title>
</head>
<body>
<div class="container">
<h2>다중파일업로드</h2>
<div class="panel panel-default">
<div class="panel-heading">스프링 이용한 다중 파일 업로드 구현 </div>
<div class="panel-body">Panel Content
<!-- 파일업로드를 위한 설정 인코딩타입 -->
<form class="form-horizontal" action="<c:url value='/upload.do'/>" enctype="multipart/form-data" method="post">
<div class="form-group">
<label class="control-label col-sm-2" for="id">아이디:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="id" name="id" placeholder="Enter id" style="width: 30%">
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="name">이름:</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="name" name="name" placeholder="Enter name">
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="">파일추가:</label>
<div class="col-sm-10">
<input type="button" value="파일 추가" onclick="file_add()"><br>
<div id="d_file"></div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">업로드</button>
</div>
</div>
</form>
</div>
<div class="panel-footer">나프2탄고</div>
</div>
</div>
</body>
</html>
form에 enctype ="multipart/form-data"로 설정해두어야 한다.
1.3 서버 구현
- 폼에 있는 데이터는 body에 파일을 추가하면 들어갈 것이다.
- id = v, Name =v /// File1 =바이너리 데이터 , File2 = 바이너리 데이터
- 이런식으로 일반 파라미터와 파일정보 동시에 보내짐
- 따라서 서버에서는 이를 구분해서 읽어야 함
@RequestMapping("/upload.do")
public String upload(MultipartHttpServletRequest multipartRequest, HttpServletRequest request) {
Map map = new HashMap(); // KEY,Value
//열거형 유사 배열
Enumeration<String> e= multipartRequest.getParameterNames(); // 첨부파일은 x id name같은것만읽음
//장점 배열과 달리 몇개인지 알필요없음 (배열길이) 있음 가지고오고 없음 말고
//데이터 유무만 판단해서 가져옴 데이터 처리에 편리함 있다.
while(e.hasMoreElements()) {
String Pname = e.nextElement(); //id
String val= multipartRequest.getParameter(Pname);
//System.out.println(Pname+" "+val);
map.put(Pname, val);
}
//파일을 담고 있는 파라미터를 읽어오기
Iterator<String> it= multipartRequest.getFileNames();// 파일이름이 아니라-> 파일 담은 파라미터이름
while(it.hasNext()) {
String paramFName =it.next();
MultipartFile mfile = multipartRequest.getFile(paramFName);//파일(이름,타입,크기...기타등등) 이걸 받아줘야함
String oName = mfile.getOriginalFilename(); //진짜 파일 이름
}
return "";
}
- MultipartHttpServletRequest에 getParameterNames()를 통해 첨부파일x id,name 같은 것만 읽음
- MultipartHttpServletRequest에 getFilenames()를 통해 파일을 담은 파라미터들을 가져올 수 있다.
이를 .getFile("파라미터명")을 통해 MultipartFile객체로 바꿔주자.
파일 내용,파일의 이름,크기,타입 기타 등등 담고 있는 클래스이다.
- 이제 오리지널 파일명을 저장할 list타입과 파일을 저장할 dir을 지정하자
@RequestMapping("/upload.do")
public String upload(MultipartHttpServletRequest multipartRequest, HttpServletRequest request, Model model) throws Exception{
String UPLOAD_DIR="epo"; // \\, /
String uploadPath=request.getServletContext().getRealPath("")+File.separator+UPLOAD_DIR;
Map map = new HashMap(); // KEY,Value
Enumeration<String> e= multipartRequest.getParameterNames(); // 첨부파일은 x id name같은것만읽음
while(e.hasMoreElements()) {
String Pname = e.nextElement(); //id
String val= multipartRequest.getParameter(Pname); //파일 쓰기
map.put(Pname, val);
}
Iterator<String> it= multipartRequest.getFileNames();// 파일이름이 아니라-> 파일 담은 파라미터이름
List<String> fileList = new ArrayList<>();
while(it.hasNext()) {
String paramFName =it.next();
MultipartFile mfile = multipartRequest.getFile(paramFName);//파일(이름,타입,크기...기타등등) 이걸 받아줘야함
String oName = mfile.getOriginalFilename(); //진짜 파일 이름
//오리지널 파일명 저장
fileList.add(oName);
//파일 업로드 폴더 업로드 디렉토리 확인
File file = new File(uploadPath+File.separator+paramFName); //file1
if(mfile.getSize()!=0) {
if(!file.exists()) { //파일이 존재하지 않으면,
if(file.getParentFile().mkdir()) { //dir생성
file.createNewFile(); // 임시로 파일을 생성한다.
}
}
mfile.transferTo(new File(uploadPath+File.separator+oName)); //파일업로드
}
}
map.put("fileList", fileList);
model.addAttribute("map", map);
return "result";
}
}
- DIR, path 지정 File 인스턴스 만들고, 넘어온 파일 사이즈가 0보다 크다면,
-> File 인스턴스 존재 확인 -> 없으면 상위폴더 생성(UPLOAD_DIR) -> 임시파일 생성
-> MultipartFile을 통한 파일 업로드
-> 클라이언트 요청으로 넘어온 일반 요청과 오리지널 파일명 담은 map을 view로 전달
2. view페이지와 파일 다운로드
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css'>
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js'></script>
<meta charset="UTF-8">
<title>결과창</title>
</head>
<body>
<div class="container">
<h2>업로드 완료</h2>
<div class="panel panel-default">
<div class="panel-heading">스프링을 이용한 다중 파일 업로드</div>
<div class="panel-body">
<table class ="table table-bordered table-hover">
<tr>
<td>아이디</td>
<td>${map.id}</td>
</tr>
<tr>
<td>이름</td>
<td>${map.name}</td>
</tr>
<c:forEach var="fName" items="${map.fileList}">
<tr>
<td>${fName} </td>
<td>다운로드</td>
</tr>
</c:forEach>
</table>
</div>
<div class="panel-footer">인퍼런 홧팅</div>
</div>
</div>
</body>
</html>
파일 업로드 완료 후 view페이지 간단구현
<tr>
<td>{fName}</td>
<td><a href="javascript:getfile('${fName}')"><span class="glyphicon glyphicon-file"></span></a></td>
</tr>
다운로드 버튼 추가
function getfile(filename){
location.href = "<c:url value='download.do'/>?filename="+filename;
}
다운로드 컨트롤러 쪽 구현 내용은 MVC07에서 했던 파일 다운로드에서 몇가지만 수정함
@RequestMapping("/download.do")
public void download(@RequestParam("filename")String filename,
HttpServletResponse response,
HttpServletRequest request
) throws Exception {
//파일 저장된 폴더 위치 및, 넘어온 파일 명으로 파일 객체생성
String UPLOAD_DIR="epo";
String uploadPath=request.getServletContext().getRealPath("")+File.separator+UPLOAD_DIR;
File f=new File(uploadPath+"\\"+filename);
// 클라이언트로 부터 넘어오는 파일이름에 한글이 있는경우 깨지지 않게하기 위함
// File에 넣을 때는 인코딩 설정안해도 괜춘한가봄
filename=URLEncoder.encode(filename, "UTF-8");
filename=filename.replace("+"," ");
// 다운로드 준비로 서버에서 클라이언트에게 다운로드 준비를 시키는 부분(다운로드 창을 띄운다)
response.setContentLength((int)f.length());
response.setContentType("application/x-msdownload;charset=utf-8");
//이것 떄문에 설정한거임 헤더에 넣을 때 한글 안깨지게 하려고
response.setHeader("Content-Disposition", "attachment;filename="+filename+";");
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
// 실제 다운로드를 하는 부분
FileInputStream in=new FileInputStream(f); //파일읽기 준비
OutputStream out=response.getOutputStream();
byte[] buffer=new byte[104];
while(true) {
int count=in.read(buffer);
if(count==-1) {
break;
}
out.write(buffer, 0, count); //다운로드(0%......100%)
}//_while_
in.close();
out.close();
}
}
참고자료: 나프2탄 (인프런) - 박매일
https://www.inflearn.com/course/%EB%82%98%ED%94%84-mvc-2