본문 바로가기
Framework/Spring

[Spring] 스프링 파일업로드/ file upload/ 파일업로드 한글깨짐

by 나비와꽃기린 2016. 9. 28.


기본적인 Controller와 jsp 경로 호출 등 셋팅은 되어있다는 가정하에 시작한다.

(필자는 STS를 사용 > http://mkil.tistory.com/267 (4)테스트환경 참조~)


1. fileTest.jsp 생성


JSP를 다음과 같이 생성하고 FORM 태그를 사용하여 다음과 같이 작성하자.

가장 중요한건 multipart/form-data를 설정해 줘야 한다는것.

주석에 순서대로 설명을 달아놓았다.


해당 소스는 "제출" 버튼을 누르면 submit동작에 의해 /fileUpload로 매핑되어져 있는

Contorller를 호출한다.

 <body>
 
 <!--
    1. 전송하고자 하는 입력 폼을 <form>과 </form>사이에 위치해놓는다.
    2. action 값은 데이터를 전송받아서 처리할 Controller url로 설정한다.
    3. method를 반드시 post로 설정해준다. get으로 설정시 parameter들이 url에 따라간다.
    4. enctype을 multipart/form-data로 설정한다.
    5. <form>과 </form>사이에 input type submit버튼을 위치시킨다.
 -->
	 <form action="/sts/fileUpload" id="fileUpload" name="fileUpload" method="post" enctype="multipart/form-data">
		이름 : <input type="text" name="name" id="cmd" value="namevla"><br>
		파일 : <input type="file" name="file"><br>
		<input type="submit" name="업로드" value="제출"><br>
	 </form>
 </body>


2. action Url에 fileupload 메소드 작성


action url이 매칭되는 /fileUpload 에 file upload 소스를 작성한다!

multipart/form-data는 서버에서 MultipartHttpServletRequest 로 받아야 한다.


아래 소스는 저장하고자 하는 물리적인 경로에 업로드한 파일의 파일명을 암호화 한뒤 저장하는 소스이다.

(c://aaa 처럼... 저장 하고 싶은 경로를 적어준다.)


DB에 저장하고 싶다면 보통 파일 Data만을 관리하는 TABLE을 생성한 뒤

파일명, 파일경로, 파일사이즈 MultipartHttpServletRequest에서 핸들링하여 추출한 뒤, service와 dao를 거쳐 INSERT 해 주면 된다.

	@RequestMapping(value = "/fileUpload") // method = RequestMethod.GET
	public Map fileUpload(HttpServletRequest req, HttpServletResponse rep) {

		//파일이 저장될 path 설정
		String path = "c://aaa";
		Map returnObject = new HashMap();
		
		try {
		// MultipartHttpServletRequest 생성
		MultipartHttpServletRequest mhsr = (MultipartHttpServletRequest) req;
		Iterator iter = mhsr.getFileNames();

		MultipartFile mfile = null;
		String fieldName = "";
		List resultList = new ArrayList();

		// 디레토리가 없다면 생성
		File dir = new File(path);
		if (!dir.isDirectory()) {
			dir.mkdirs();
		}

		// 값이 나올때까지
		while (iter.hasNext()) {
			fieldName = iter.next(); // 내용을 가져와서
			mfile = mhsr.getFile(fieldName);
			String origName;
			
			origName = new String(mfile.getOriginalFilename().getBytes("8859_1"), "UTF-8"); //한글꺠짐 방지
			// 파일명이 없다면
			if ("".equals(origName)) {
				continue;
			}

			// 파일 명 변경(uuid로 암호화)
			String ext = origName.substring(origName.lastIndexOf('.')); // 확장자
			String saveFileName = getUuid() + ext;

			// 설정한 path에 파일저장
			File serverFile = new File(path + File.separator + saveFileName);
			mfile.transferTo(serverFile);
			
			Map file = new HashMap();
			file.put("origName", origName);
			file.put("sfile", serverFile);
			resultList.add(file);
		}
		
		returnObject.put("files", resultList);
		returnObject.put("params", mhsr.getParameterMap());

		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}

        //uuid생성
	public static String getUuid() {
		return UUID.randomUUID().toString().replaceAll("-", "");
	}


이렇게 작성하고 룰루랄라 실행을 해보면 

다음과 같은 에러를 뿜게 될 것이다.ㅡㅡㅋㅋㅋ

왜냐하면 설정을 빠뜨렸기 떄문이다.

Spring에서 file upload를 사용하기 위해서는 설정파일에 Multipart에 관련된 설정을 꼭 해줘야 한다.

java.lang.ClassCastException: org.apache.catalina.connector.RequestFacade cannot be cast to org.springframework.web.multipart.MultipartHttpServletRequest
	com.example.sts.TestController.fileUpload(TestController.java:31)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:483)
	org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
	org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
	org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
	org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)


3. servelet-context.xml 에 multipartResolver 설정 추가



*** Spring에서는 encType="multipart/form-data" 을 사용하기 위해 CommonsMultipartResolver 를 꼭 설정해줘야 한다.


COmmonsMultipartResolver는 스프링에서 파일업로드 기능을 구현해놓은 클래스이다. 해당 클래스가 file upload를 처리해 주며, maxUploadSize는 이름만 봐도 추측이 가능하다. 바로 서버에 업로드 할 수 있는 첨부파일의 최대 크기를 의미하는데 단위는 byte이다. 알맞게 설정해 주면 된다.

<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <beans:property  name="maxUploadSize" value="10000000" />
    </beans:bean>

또한, 해당 크기 이상의 파일을 첨부하면 자동으로 Spring에서 에러를 떨궈주게 된다. 


4. pom.xml 에 dependency 추가




COmmonsMultipartResolver를 추가했음에도 file upload에 다음과 같은 에러가 난다면..............

java.lang.NoClassDefFoundError: org/apache/commons/fileupload/FileItemFactory
	java.lang.Class.getDeclaredConstructors0(Native Method)
	java.lang.Class.privateGetDeclaredConstructors(Class.java:2663)
	java.lang.Class.getDeclaredConstructors(Class.java:2012)
	org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:229)
	org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:962)
	org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:935)
	org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
	org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)

pom.xml에 dependency를 추가해 줬나 확인해보자.

<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>

5. 결과확인



파일을 업로드 하게 되면...................................






설정해던 물리적인 path에 암호화된 파일명이 다음과 같이 저장된다 !







6. 번외 _ DB 저장 예제



음... 이 예제는 설명 용은 아니고............

나중에 제가 다시 참고하기 위해  혹은 다른분들 참조하시라고 올려놓는 소스입니다~


"저장"버튼을 누르면 ---> 물리적인 경로에 파일을 저장 + DB에 데이터 저장 을 같이 하는 예제입니다.


1. fileUploadPath는 상단에서 properties파일에서 가져오며 fileUpload 메소드를 통해 물리적인 위치를 저장시키고, fileUpload 메소드에서 return받은 값들을 List에 저장시킨다.


2. List에 담겨있는 files의 사이즈를 체크하여 file이 첨부가 되어있다면 (0보다 크다면)

files에 담겨있는 파라미터들을 추출해, 파일관련 TABLE에 INSERT해준다.




@RequestMapping(value = "/board/data/InsertBoardData")   
public @ResponseBody Map InsertBoardData(HttpServletRequest req, HttpServletResponse res) {
	
	Map fileInfo = null;
	DataEntity param = HttpUtil.getServletRequestParam(req);
	PortletUtil.setUserInfo(param);
	
	 try {
		 
		 fileInfo = HttpUtil.fileUpload(req, fileUploadPath); //해당 디렉토리 위치에 파일 업로드
		 List files = (List)fileInfo.get("files");         

		 /* 게시판 data insert */
		 param.put("attach", ( files.size() > 0 ? "Y" : "N") );
		 int cnt = boardService.insertBoard(param);
		 
		 if( files.size() > 0 ){
			
			param.put("file_gbn", "BD");
			param.put("file_id", fileService.insertBDFileKey(param));	// 파일 DB KEY 조회
			param.put("docnum", boardService.selectMaxDocnum());        // 게시글 MAX DOCNUM 조회
			
			 for(int i=0;i<files.size();i++){
				Map file = (Map)files.get(i);

				String origName = (String)file.get("origName");
				File sFile = (File)file.get("sfile");
				
				param.put("file_name", sFile.getName());          //복호화된 파일 이름
				param.put("file_path", sFile.getAbsolutePath());  //물리적 저장 경로
				param.put("file_size", sFile.length());           //파일 크기
				param.put("file_orig", origName);  				  //원래 파일 명
				
				fileService.insertBDFile(param);
			 }
		 }
	 
	  } catch (IllegalStateException e) {
		 e.printStackTrace();
		 
	  }catch (IOException e) {
		 e.printStackTrace();
	  }
	 
	  return null;
}

댓글4