Web/Spring

Spring MVC02 - 다중파일 업로드

now0204 2023. 6. 30. 23:23

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

 

[NarP Series] MVC 프레임워크는 내 손에 [나프2탄] - 인프런 | 강의

본 과정은 WEB MVC 프레임워크가 무엇이고 WEB MVC 프레임워크가 어떻게 변형이 되면서 Spring WEB MVC 프레임워크로 넘어가는지 TPC(생각하고-표현하고-코딩하고) 기법을 통해 단계적으로 학습하고 이

www.inflearn.com