개발/스프링

20. 스프링 프레임워크 - 다중 파일 업로드 기능[자바 웹을 다루는 기술]

괴발자-K 2022. 5. 30. 10:48

파일 첨부 기능에 대해 알아보록 하겠습니다. 

웹이나 메일에서 이미지, 파일 등등을 첨부하는 경우가 있습니다. 

근데 한 개만 첨부하는 것이 아니라 한 개 이상의 여러개를 첨부하는 상황이 있습니다.

그래서 오늘은 다중 파일을 업로드 하는 기능을 구현하도록 하겠습니다.

 

스프링의 CommonsMultipartResolver 클래스를 이용하면 다중 파일 업로드가 가능합니다.

 

CommonsMultipartResolver 클래스 속성

속성 설명
maxUploadSize 최대로 업로드가 가능한 파일의 크기를 설정합니다.
maxInMemorySize 디스크에 임시 파일을 생성하기 전 메모리에 보관할 수 있는 최대 바이트 크기를 설정합니다.
defaultEncoding 전달되는 매개변수의 인코딩을 설정합니다.

 

pom.xml

<!-- 파일 업로드  -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.2.1</version>
    </dependency>
    <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
         <version>1.4</version>
    </dependency>

파일업로드에 필요한 라이브러리를 pom.xml에 설정 

 

servlet-context.xml

<beans:bean id="multipartResolver" 
	                class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       <beans:property name="maxUploadSize" value="52428800"/>
       <beans:property name="maxInMemorySize" value="1000000"/>
       <beans:property name="defaultEncoding" value="utf-8"/>
</beans:bean>

servlet-context.xml 에서 CommonsMultipartResolver 클래스를 multipartResolver 빈으로 설정

 

파일 업로드/ 다운로드 컨트롤러 클래스 파일 생성

 

FileDownloadController.java

package com.myspring.pro28.ex01;

...................
@Controller
public class FileDownloadController {
    
	//파일 저장 위치를 지정
	private static String CURR_IMAGE_REPO_PATH = "c:\\spring\\image_repo";
	
	
	//다운로드할 이미 파일 이름을 전달
	@RequestMapping("/download")
	protected void download(@RequestParam("imageFileName") String imageFileName,
			                 HttpServletResponse response) throws Exception{
		OutputStream out = response.getOutputStream();
		String downFile = CURR_IMAGE_REPO_PATH + "\\" + imageFileName;
		//다운로드한 파일 객체를 생성
		File file = new File(downFile);
		
		response.setHeader("Cache-Control", "no-cache");
		//헤더에 파일 이름을 설정
		response.addHeader("Content-disposition", "attachment; fileName="+imageFileName);
		
		FileInputStream in = new FileInputStream(file);
		byte[] buffer = new byte[1024 * 8];
		
		//버퍼를 이용해 한 번에 8kbyte씩 브라우저에 전송
		while(true) {
			int count = in.read(buffer);
			if(count == -1) break;
			out.write(buffer,0,count);
		}
		in.close();
		out.close();
	}
}

버퍼 기능을 이용해 브아루저로 이미지파일을 전송합니다.

 

FileUploadController.java

package com.myspring.pro28.ex01;

.............................

@Controller
public class FileUploadController {
   
	private static final String CURR_IMAGE_REPO_PATH = "c:\\spring\\image_repo";
	
	//업로드 창인 uploadForm.jsp를 반환
	@RequestMapping(value="/form")
	public String form() {
		return "uploadForm";
	}
	
	@RequestMapping(value="/upload", method = RequestMethod.POST)
	public ModelAndView upload(MultipartHttpServletRequest multipartRequest,
			                   HttpServletResponse response) throws Exception{
	    
		multipartRequest.setCharacterEncoding("utf-8");
		// 매개변수 정보와 파일 정보를 저장할 Map을 생성
		Map map = new HashMap();
		// 파라미터 값을 한꺼번에 받아옴
		Enumeration enu = multipartRequest.getParameterNames();
	    while(enu.hasMoreElements()) {
	    	String name = (String)enu.nextElement();
	    	String value = multipartRequest.getParameter(name);
	    	map.put(name, value);
	    }
	    List fileList = fileProcess(multipartRequest);
	    map.put("fileList", fileList);
	    ModelAndView mav = new ModelAndView();
		mav.addObject("map", map);
		mav.setViewName("result");
		
		return mav;
	}
	
	private List<String> fileProcess(MultipartHttpServletRequest multipartRequest) throws Exception{
		
		List<String> fileList = new ArrayList<String>();
		// 첨부된 파일 이름을 가져옵니다.
		Iterator<String> fileNames = multipartRequest.getFileNames();
		while(fileNames.hasNext()) {
			String fileName = fileNames.next();
			//파일 이름에 대한 MultipartFile 객체를 가져옮
			MultipartFile mFile = multipartRequest.getFile(fileName);
			//실제 파일 이름을 가져옴
			String originalFileName = mFile.getOriginalFilename();
			//파일 이름을 하나씩 저장 list
			fileList.add(originalFileName);
			File file = new File(CURR_IMAGE_REPO_PATH + "\\" + fileName);
			//첨부된 파일이 있는지 체크 합니다.
			//경로에 파일이 없으면 그 경로에 해당하는 디렉터리를 만든후 파일을 생성
			if(mFile.getSize() != 0) {
				if(!file.exists()) {
					if(file.getParentFile().mkdirs()) {
						file.createNewFile();
					}
				}
				//임시로 저장된 multipartFile을 실제 파일로 전송
				mFile.transferTo(new File(CURR_IMAGE_REPO_PATH+"\\"+originalFileName));
			}
		}
		return fileList;
	}
	
}

파일과 파라미터를 같이 전달해야 하므로 Map을 사용합니다.

파라미터의 name과 value를 map의 key와 value로 저장합니다.

파일이름을 list에 저장한 후 다시 map에 key/value러 저장합니다.

 

JSP 설정

 

uploadForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath }"/>
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
    //파일 업로드 name값을 다르게 하는 변수
    var cnt=1;
    function fn_addFile() {
		$("#d_file").append("<br>"+"<input type='file' name='file"+cnt+"'/'>");
		cnt++;
	}
</script>
<meta charset="UTF-8">
<title>파일 업로드 하기</title>
</head>
<body>
     <h1>파일 업로드 하기</h1>
     <!-- 파일 업로드 시 encType은 반드시 multipart/form-data로 설정 해야합니다.  -->
     <form action="${contextPath }/upload" method="post" enctype="multipart/form-data">
     <label>아이디:</label>
        <input type="text" name="id"><br>
     <label>이름:</label>
        <input type="text" name="name"><br>
        <!--파일 추가를 클릭하면 동적으로 파일 업로드를 추가  -->
        <input type="button" value="파일 추가" onClick="fn_addFile()"><br>
        <!--자바스크립트를 이용해 <div> 태그 안에 파일 업로드를 추가  -->   
        <div id="d_file">
        </div>
        <input type="submit" value="업로드"/>
     </form>
</body>
</html>

 

result.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath }"/>
    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>결과창</title>
</head>
<body>
    <h1>업로드가 완료되었습니다.</h1>
    <label>아이디:</label>
    <input type="text" name="id" value="${map.id }" readonly><br>
    <label>이름 :</label>
    <input type="text" name="name" value="${map.name }" readonly><br>
    
    <!--업로드한 파일들을 forEach문을 이용해 <img> 태그에 표시함  -->
    <div class="result-images">
    <c:forEach var="imageFileName" items="${map.fileList }">
       <img src="${contextPath }/download?imageFileName=${imageFileName}">
       <br><br>
    </c:forEach>
    </div>
    <a href='${contextPath }/form'>다시 업로드 하기</a>
</body>
</html>

 

 

#마무리

다중파일 업로드 기능은 실제로 많이 쓰이는 기능입니다. 해당 기능을 응용하여 사용하시길 바랍니다.