본 내용은 혼자 공부하기 위하여 다른 포스트를 보면서 저에게 필요한 부분과 궁금했던 부분을 추가하여 게시하는 곳입니다.
궁금한 것에 대한 것은 모르는 것이 많겠지만 함께 알아보도록 노력하겠습니다.
참조 게시 포스트 : http://addio3305.tistory.com/
------------------------------------------------------------------------------------------------------------------------------------------
1. 파일 업로드
파일 업로드를 위해서 먼저 DB에 파일을 위한 테이블을 만든다.
1.1 SQL
CREATE TABLE TB_FILE1 ( IDX INT PRIMARY KEY AUTO_INCREMENT, BOARD_IDX INT NOT NULL, ORIGINAL_FILE_NAME VARCHAR(260) NOT NULL, STORED_FILE_NAME VARCHAR(36) NOT NULL, FILE_SIZE INT, CREA_DTM TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CREA_ID VARCHAR(30) NOT NULL, DEL_GB VARCHAR(1) NOT NULL DEFAULT 'N' );
- IDX는 파일의 고유 번호, BOARD_IDX는 게시판의 번호이다. ORIGINAL_FILE_NAME은 그 파일 업로드 시의 파일 이름, STORED_FILE_NAME은 서버에 저장할 때 파일 이름으로 겹치는 이름을 피하고자 만든 컬럼이다. 나머지 컬럼들은 설명 할 필요없다고 생각한다.
1.2 context-common.xml
resource/config/spring 경로에 context-common.xml을 생성해준다.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <!-- MultipartResolver 설정 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="100000000" /> <property name="maxInMemorySize" value="100000000" /> </bean> </beans>
- MultipartResolver를 선언해주는 곳으로 파일 업로드 기능을 하는 클래스이다. property의 value 값은 파일 사이즈를 말한다.
1.3 JSP
이번에는 JSP 파일부터 손보려고 한다. 먼저 게시글 쓰기 페이지에 파일 업로드 하는 부분을 만들 것이다.
<html> <head> <title>글쓰기</title> <%@ include file="/WEB-INF/include/include-header.jspf" %> </head> <body> <form id="frm" enctype="multipart/form-data" > <table class="board_view"> <colgroup> <col width="15%" > <col width="*" > </colgroup> <caption>게시글 작성</caption> <tbody> <tr> <th scope="row">제목</th> <td><input type="text" name="TITLE" class="wdp_90" /></td> </tr> <tr> <th scope="row">내용</th> <td><textarea cols="100" rows="20" id="CONTENTS" name="CONTENTS" title="내용"></textarea></td> </tr> </tbody> </table> <div id="fileDiv"> <p> <input type="file" name="file_0"/> </p> </div> <br/><br/> <a href="#this" id="add" class="btn">파일 추가하기</a> <a href="#this" id="list" class="btn">목록으로</a> <a href="#this" id="write" class="btn">글쓰기</a> </form> <%@ include file="/WEB-INF/include/include-body.jspf" %> <script type="text/javascript"> $(document).ready(function(){ $("#list").on("click",function(e){ e.preventDefault(); fn_openBoardList(); }) $("#write").on("click",function(e){ e.preventDefault(); fn_writeBoard(); }) }); function fn_openBoardList(){ var comSubmit = new ComSubmit(); comSubmit.setUrl("<c:url value='/sample/openBoardList.do'/>"); comSubmit.submit(); } function fn_writeBoard(){ var comSubmit = new ComSubmit("frm"); comSubmit.setUrl("<c:url value='/sample/writeBoard.do'/>"); comSubmit.submit(); } </script> </body> </html>
- 주의 깊게 볼 곳은 7, 25~30줄이다.
7줄의 경우는 form의 타입을 설정해줌으로써 multipart 형식이 된다. multipart 형식은 글자를 제외한 그림 같은 파일을 말한다. 25~30줄의 경우는 파일 업로드 시의 사용되는 input을 선언해둔 것이다.
1.4 업로드 확인
- 파일 선택으로 보냈을 때 서버로 전송이 되는지 확인을 Controller와 Service의 코드를 수정하겠다.
SampleController.java
@RequestMapping(value="/sample/writeBoard.do") public ModelAndView writeBoard(CommandMap commandMap, HttpServletRequest req) throws Exception{ ModelAndView mv = new ModelAndView("redirect:/sample/openBoardList.do"); sampleService.writeBoard(commandMap.getMap(),req); return mv; }
- 변경된 점은 HttpServletRequest가 파라미터로 추가되고 이 파라미터를 Service 단으로 던져주는 것이다.
우리가 화면에서 전송한 모든 데이터는 HttpServletRequest에 담겨서 전송되고, 그것을 HandlerMethodArgumentResolver를 이용하여 CommandMap에 담아주었다. 그렇지만 첨부파일은 CommandMap에서 처리를 하지 않았기 때문에 HttpServletRequest를 추가로 받도록 하였다. 이 HttpServletRequest에는 모든 텍스트 데이터 뿐만이 아니라 화면에서 전송된 파일 정보도 함께 담겨있다. 우리는 CommandMap을 이용하여 텍스트 데이터는 처리하기 때문에, HttpServletRequest는 파일 정보만 활용할 계획이다.
SampleService.java
void writeBoard(Map<String,Object> map, HttpServletRequest req) throws Exception;
SampleServiceImpl.java
@Override public void insertBoard(Map<String, Object> map, HttpServletRequest request) throws Exception { sampleDAO.insertBoard(map); MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest)request; Iterator<String> iterator = multipartHttpServletRequest.getFileNames(); MultipartFile multipartFile = null; while(iterator.hasNext()){ multipartFile = multipartHttpServletRequest.getFile(iterator.next()); if(multipartFile.isEmpty() == false){ log.debug("------------- file start -------------"); log.debug("name : "+multipartFile.getName()); log.debug("filename : "+multipartFile.getOriginalFilename()); log.debug("size : "+multipartFile.getSize()); log.debug("-------------- file end --------------\n"); } } }
이렇게 작성하면 위와 같은 화면이 될 것이다. 위 화면에 파일 선택하여 사진을 골라 제목과 내용을 작성한 뒤 글쓰기를 해보자. 그리고 로그를 확인해보면 밑과 같이 뜰 것이다.
- 위 로그를 보면 정상적으로 서버로 파일이 전송된다는 것을 확인할 수 있다. 그러면 본격적으로 업로드를 하여 서버로 전송하여 서버에서 파일을 받아보자.
2. FileUtils 클래스 생성
java/common/util 경로에 FileUtils, CommonUtils 클래스를 생성하자. 그리고 다음 코드를 작성하자.
CommonUtils.java
public class CommonUtils { public static String getRandomString() { return UUID.randomUUID().toString().replaceAll("-",""); } }
해당 클래스의 메서드는 랜덤한 32글자의 문자열을 만들어 리턴해주는 기능으로 아까 DB에서 STORED_FILE_NAME으로 지정하고 서버에 저장하기 위한 문자열이다.
FileUtils.java
@Component("fileUtils") public class FileUtils { String filePath = "C:\\dev1\\file\\"; Logger log = Logger.getLogger(this.getClass()); public List<Map<String,Object>> parseInsertFileInfo(Map<String,Object> map, HttpServletRequest req) throws Exception{ MultipartHttpServletRequest mulReq = (MultipartHttpServletRequest)req; String original_Name=null; String original_Extension=null; String stored_Name=null; MultipartFile mulFile = null; Iterator<String> iterator= mulReq.getFileNames(); List<Map<String,Object>> fileList = new ArrayList<Map<String,Object>>(); Map<String,Object> fileMap = null; String board_IDX = (String) map.get("IDX").toString(); File file = new File(filePath); if(file.exists()==false) { file.mkdirs(); } while(iterator.hasNext()) { mulFile=mulReq.getFile(iterator.next()); if(mulFile.isEmpty()==false) { original_Name=mulFile.getOriginalFilename(); original_Extension=mulFile.getOriginalFilename().substring(original_Name.lastIndexOf(".")); stored_Name=CommonUtils.getRandomString()+original_Extension; file = new File(filePath+stored_Name); mulFile.transferTo(file); fileMap = new HashMap<String,Object>(); fileMap.put("BOARD_IDX", board_IDX); fileMap.put("ORIGINAL_FILE_NAME", original_Name); fileMap.put("STORED_FILE_NAME", stored_Name); fileMap.put("FILE_SIZE", mulFile.getSize()); fileList.add(fileMap); } } return fileList; } }
@Resource(name="fileUtils") private FileUtils fileUtils; @Override public void writeBoard(Map<String, Object> map, HttpServletRequest req) throws Exception { sampleDAO.writeBoard(map); List<Map<String,Object>> list = fileUtils.parseInsertFileInfo(map,req); for(int i=0; i<list.size();i++) { sampleDAO.insertFile(list.get(i)); } }
- 아까 위에서 만든 스프링 객체를 자동주입으로 선언하고 작성한 메서드를 이용하여 파일이 포함된 리스트를 반환 받는다. 그리고 반환 받은 파일 리스트 하나하나를 DAO를 통해 DB에 저장한다.
SampleDAO.java
public void insertFile(Map<String, Object> map) { insert("sample.insertFile",map); }
- 다른 DAO 메서드와 크게 차이는 없다.
sample_SQL.xml
<insert id="sample.insertFile" parameterType="hashmap"> <![CDATA[ INSERT INTO TB_FILE1 ( BOARD_IDX, ORIGINAL_FILE_NAME, STORED_FILE_NAME, FILE_SIZE, CREA_ID ) VALUES ( #{BOARD_IDX}, #{ORIGINAL_FILE_NAME}, #{STORED_FILE_NAME}, #{FILE_SIZE}, 'ADMIN' ) ]]> </insert>
- 아까 FileUtils 클래스에서 #{BOARD_IDX} 등 4개의 값들을 선언하여 DAO를 통해 보내주었기 때문에 여기서 사용이 가능하다.
그리고 다음으로 sample.writeBoard 부분 을 고쳐야한다. 다음과 같이 고쳐주자.
<insert id="sample.writeBoard" parameterType="hashmap" useGeneratedKeys="true" keyProperty="IDX" > <![CDATA[ INSERT INTO TB_BOARD1( IDX, TITLE, CONTENTS, HIT_CNT, DEL_GB, CREA_ID) VALUES( #{IDX}, #{TITLE}, #{CONTENTS}, 0, 'N', 'ADMIN') ]]> </insert>
- useGeneratedKeys, keyProperty 의 속성을 추가하였는데 useGeneratedKeys 란 자동 생성키를 사용하겠느냐로 여기서 만들어진 IDX, 즉 게시물 번호를 말한다. keyProperty가 그 해당 자동 생성키의 이름을 설정하는 것으로 이를 통해 위에 FileUtils에서 Board_IDX 값을 받아왔다.
여기까지 작성 후 테스트 하여 보자.
- 다음과 같이 작성하여 글쓰기를 처리하였다.
- 별 문제 없이 게시물 리스트에 등록됨을 확인하였다.
- 로그를 통해서도 파일이 잘 전송되는 것을 확인할 수 있다.
- DB에서도 해당 파일의 정보가 삽입된 것을 확인할 수 있다.
- 마지막으로 아까 정해둔 경로를 통해 랜덤한 문자열로 등록된 이미지 파일을 볼 수 있다.
'코딩 > Spring' 카테고리의 다른 글
Spring 개발 - 게시판 만들기(15) - 파일 다중 업로드 및 게시판 수정하기(파일 업로드) (0) | 2018.01.02 |
---|---|
Spring 개발 - 게시판 만들기(14) - 파일 다운로드 (0) | 2018.01.01 |
Spring 개발 - 게시판 만들기(12) - 게시글 수정하기, 삭제하기 (1) | 2017.12.26 |
Spring 개발 - 게시판 만들기(11) - 게시글 상세보기 (0) | 2017.12.26 |
Spring 개발 - 게시판 만들기(10) - 게시글 쓰기 (0) | 2017.12.25 |