티스토리 뷰
| File Upload
Spring framework에 File Upload 기능을 구현해보자.
|| Setting
Spring framework에 File Upload 기능을 구현하기 위해
Library를 추가해주자.
project/pom.xml
1 2 3 4 5 6 | <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> | cs |
maxUploadSize는 최대 업로드 가능한 파일의 바이트 크기
maxInMemorySize는 디스크에 임시 파일을 생성하기 전 메모리에 보관할 수 있는 최대 바이트 크기
project/src/main/webapp/WEB-INF/spring/appServlet/servelt-context.xml
1 2 3 4 5 6 | <!-- fileUpload --> <beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <beans:property name="defaultEncoding" value="UTF-8"/> <beans:property name="maxUploadSize" value="52428800"/> <!-- 50MB --> <beans:property name="maxInMemorySize" value="1048576"/> <!-- 1MB --> </beans:bean> | cs |
||| DataBase
1 2 3 4 5 6 7 8 | CREATE TABLE `file_info` ( `no` int NOT NULL AUTO_INCREMENT, `articleno` varchar(45) DEFAULT NULL, `saveFolder` varchar(45) DEFAULT NULL, `originFile` varchar(45) DEFAULT NULL, `saveFile` varchar(45) DEFAULT NULL, PRIMARY KEY (`no`) ) | cs |
1 2 3 4 5 6 7 8 9 10 | CREATE TABLE `guestbook` ( `articleno` int NOT NULL AUTO_INCREMENT, `userid` varchar(16) NOT NULL, `subject` varchar(100) NOT NULL, `content` varchar(2000) NOT NULL, `regtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`articleno`), KEY `guestbook_username_FK_idx` (`userid`), CONSTRAINT `guestbook_userid_FK` FOREIGN KEY (`userid`) REFERENCES `member` (`userid`) ) | cs |
||| View
form 설정
method는 "post"
enctype은 "multipart/form-date"
파일을 업로드하는 <input> tag 설정
type="file"
name="upfile"
project/src/main/webapp/WEB-INF/views/guestbook/write.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <form id="writeform" method="post" enctype="multipart/form-data" action=""> <div class="form-group" align="left"> <label for="subject">제목:</label> <input type="text" class="form-control" id="subject" name="subject"> </div> <div class="form-group" align="left"> <label for="content">내용:</label> <textarea class="form-control" rows="15" id="content" name="content"></textarea> </div> <div class="form-group" align="left"> <label for="subject">파일:</label> <input type="file" class="form-control-file border" name="upfile" multiple="multiple"> </div> <button type="button" id="writeBtn" class="btn btn-primary">글작성</button> <button type="reset" class="btn btn-warning">초기화</button> </form> | cs |
project/src/main/webapp/WEB-INF/views/guestbook/list.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <c:if test="${articles.size() != 0}"> <c:forEach var="article" items="${articles}"> <table class="table table-active"> <tbody> <tr class="table-info"> <td>작성자 : ${article.userid}</td> <td align="right">작성일 : ${article.regtime}</td> </tr> <tr> <td colspan="2" class="table-danger"><strong>${article.articleno}. ${article.subject}</strong></td> </tr> <tr> <td colspan="2">${article.content}</td> </tr> <tr> <td colspan="2"> <ul> <c:forEach var="file" items="${article.fileInfos}"> <li>${file.originFile}<a href="#" class="filedown" sfolder="${file.saveFolder}" sfile="${file.saveFile}" ofile="${file.originFile}">[다운로드]</a> <img src="${root}/resources/upload/${file.saveFolder}/${file.saveFile}"> </c:forEach> </ul> </td> </tr> <c:if test="${userinfo.userid == article.userid}"> <tr> <td colspan="2"><a href="${root}/article/modify?articleno=${article.articleno}">수정</a> <a href="${root}/article/delete?articleno=${article.articleno}">삭제</a> </td> </tr> </c:if> </tbody> </table> </c:forEach> <table> <tr> <td>${navigation.navigator}</td> </tr> </table> </c:if> | cs |
1 2 3 4 5 6 7 8 | //file download $('.filedown').click(function() { alert("원본 : " + $(this).attr('ofile') + " 실제 : " + $(this).attr('sfile')); $(document).find('[name="sfolder"]').val($(this).attr('sfolder')); $(document).find('[name="ofile"]').val($(this).attr('ofile')); $(document).find('[name="sfile"]').val($(this).attr('sfile')); $('#downform').attr('action', '${root}/article/download').attr('method', 'get').submit(); }); | cs |
||| DTO
project/src/main/java/com/cristoval/guestbook/model/GuestBookDto.java
1 2 3 4 5 6 7 8 9 10 | public class GuestBookDto { private int articleno; private String userid; private String subject; private String content; private String regtime; private List<FileInfoDto> fileInfos; } | cs |
project/src/main/java/com/cristoval/guestbook/model/FileInfoDto.java
1 2 3 4 5 6 | public class FileInfoDto { private String saveFolder; private String originFile; private String saveFile; } | cs |
||| Controller
MultiPartFile[] 형태로 여러 file을 Parameter로 받을 수 있다.
파일은 중복된 파일명이 업로드될 수 있으므로 UUID를 활용하여 저장해준다.
fileName.transferTo 메서드를 통해 설정한 저장 위치에 UUID로 변경된 파일명으로 저장해준다.
project/src/main/java/com/cristoval/guestbook/controller/GuestBookController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | @Controller @RequestMapping("/article") public class GuestBookController { @Autowired ServletContext servletContext; @Autowired private GuestBookService guestBookService; @RequestMapping(value = "/write", method = RequestMethod.GET) public String write() { return "guestbook/write"; } @RequestMapping(value = "/write", method = RequestMethod.POST) public String write(GuestBookDto guestBookDto, @RequestParam("upfile") MultipartFile[] files, Model model, HttpSession session) throws IllegalStateException, IOException { MemberDto memberDto = (MemberDto) session.getAttribute("userinfo"); if (memberDto != null) { String realPath = servletContext.getRealPath("/resources/upload"); String today = new SimpleDateFormat("yyMMdd").format(new Date()); String saveFolder = realPath + File.separator + today; System.out.println(saveFolder); File folder = new File(saveFolder); if(!folder.exists()) folder.mkdirs(); List<FileInfoDto> fileInfos = new ArrayList<FileInfoDto>(); for (MultipartFile mfile : files) { FileInfoDto fileInfoDto = new FileInfoDto(); String originalFileName = mfile.getOriginalFilename(); if (!originalFileName.isEmpty()) { String saveFileName = UUID.randomUUID().toString() + originalFileName.substring(originalFileName.lastIndexOf('.')); fileInfoDto.setSaveFolder(today); fileInfoDto.setOriginFile(originalFileName); fileInfoDto.setSaveFile(saveFileName); System.out.println(mfile.getOriginalFilename() + " " + saveFileName); mfile.transferTo(new File(folder, saveFileName)); } fileInfos.add(fileInfoDto); } guestBookDto.setFileInfos(fileInfos); guestBookDto.setUserid(memberDto.getUserid()); try { guestBookService.writeArticle(guestBookDto); return "guestbook/writesuccess"; } catch (Exception e) { e.printStackTrace(); model.addAttribute("msg", "글작성중 문제가 발생했습니다."); return "error/error"; } } else { model.addAttribute("msg", "로그인 후 사용 가능한 페이지입니다."); return "error/error"; } } } | cs |
||| Service
project/src/main/java/com/cristoval/guestbook/model/service/GuestBookServiceImpl.java
1 2 3 4 5 6 7 8 9 10 | @Override @Transactional public void writeArticle(GuestBookDto guestBookDto) throws Exception { if(guestBookDto.getSubject() == null || guestBookDto.getContent() == null) { throw new Exception(); } GuestBookMapper guestBookMapper = sqlSession.getMapper(GuestBookMapper.class); guestBookMapper.writeArticle(guestBookDto); guestBookMapper.fileRegister(guestBookDto); } | cs |
||| Mapper
파일 업로드
project/src/main/resources/mapper/guestbook.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <insert id="writeArticle" parameterType="GuestBookDto"> insert into guestbook (userid, subject, content, regtime) values (#{userid}, #{subject}, #{content}, now()) <selectKey resultType="int" keyProperty="articleno" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> </insert> <insert id="fileRegister" parameterType="GuestBookDto"> insert into file_info (articleno, savefolder, originfile, savefile) values <foreach collection="fileInfos" item="fileinfo" separator=" , "> (#{articleno}, #{fileinfo.saveFolder}, #{fileinfo.originFile}, #{fileinfo.saveFile}) </foreach> </insert> <resultMap type="GuestBookDto" id="guestBookList"> <result property="articleno" column="articleno"/> <result property="userid" column="userid"/> <result property="subject" column="subject"/> <result property="content" column="content"/> <result property="regtime" column="regtime"/> <collection property="fileInfos" column="articleno" javaType="list" ofType="FileInfoDto" select="fileInfoList"/> </resultMap> | cs |
업로드 파일을 list에 출력
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <select id="listArticle" parameterType="map" resultMap="guestBookList"> select articleno, userid, subject, content, regtime from guestbook <if test="word != null and word != ''"> <if test="key == 'subject'"> where subject like concat('%', #{word}, '%') </if> <if test="key != 'subject'"> where ${key} = #{word} </if> </if> order by articleno desc limit #{start}, #{spp} </select> <select id="fileInfoList" resultType="FileInfoDto"> select savefolder, originfile, savefile from file_info where articleno = #{articleno} </select> | cs |
||| Result
글작성 페이지
글 리스트
| File Upload
|| Setting
project/src/main/webapp/WEB-INF/spring/appServlet/servelt-context.xml
1 2 3 4 5 | <!-- fileDownload --> <beans:bean id="fileDownLoadView" class="com.cristoval.guestbook.controller.FileDownLoadView"/> <beans:bean id="fileViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"> <beans:property name="order" value="0" /> </beans:bean> | cs |
||| FileDownLoadView
project/src/main/java/com/cristoval/guestbook/controller/FileDownLoadView.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | public class FileDownLoadView extends AbstractView { public FileDownLoadView() { setContentType("apllication/download; charset=UTF-8"); } @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { ServletContext ctx = getServletContext(); String realPath = ctx.getRealPath("/resources/upload"); Map<String, Object> fileInfo = (Map<String, Object>) model.get("downloadFile"); // 전송받은 모델(파일 정보) String saveFolder = (String) fileInfo.get("sfolder"); // 파일 경로 String originalFile = (String) fileInfo.get("ofile"); // 원본 파일명(화면에 표시될 파일 이름) String saveFile = (String) fileInfo.get("sfile"); // 암호화된 파일명(실제 저장된 파일 이름) File file = new File(realPath + File.separator + saveFolder, saveFile); response.setContentType(getContentType()); response.setContentLength((int) file.length()); String header = request.getHeader("User-Agent"); boolean isIE = header.indexOf("MSIE") > -1 || header.indexOf("Trident") > -1; String fileName = null; // IE는 다르게 처리 if (isIE) { fileName = URLEncoder.encode(originalFile, "UTF-8").replaceAll("\\+", "%20"); } else { fileName = new String(originalFile.getBytes("UTF-8"), "ISO-8859-1"); } response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";"); response.setHeader("Content-Transfer-Encoding", "binary"); OutputStream out = response.getOutputStream(); FileInputStream fis = null; try { fis = new FileInputStream(file); FileCopyUtils.copy(fis, out); } catch (Exception e) { e.printStackTrace(); } finally { if(fis != null) { try { fis.close(); }catch (IOException e) { e.printStackTrace(); } } } out.flush(); } } | cs |
||| Controller
project/src/main/java/com/cristoval/guestbook/controller/GuestBookController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @RequestMapping(value="/download", method=RequestMethod.GET) public ModelAndView downloadFile(@RequestParam("sfolder") String sfolder, @RequestParam("ofile") String ofile, @RequestParam("sfile") String sfile, HttpSession session) { MemberDto memberDto = (MemberDto) session.getAttribute("userinfo"); if(memberDto != null) { Map<String, Object> fileInfo = new HashMap<String, Object>(); fileInfo.put("sfolder", sfolder); fileInfo.put("ofile", ofile); fileInfo.put("sfile", sfile); System.out.println(sfolder + " " + ofile + " " + sfile); return new ModelAndView("fileDownLoadView", "downloadFile", fileInfo); } else { return new ModelAndView("redirect:/"); } } | cs |
||| Result
'Web > Spring' 카테고리의 다른 글
Github에 있는 SpringBoot(maven, gradle) Project Repository import하기(STS, Eclipse) (3) | 2021.04.26 |
---|---|
Local에 있는 SpringBoot(maven, gradle) project import하기(STS, Eclipse) (0) | 2021.04.26 |
[Spring-Boot] 게시판 CRUD (0) | 2020.11.17 |
[Spring-Boot] Spring, Spring-Boot 기본 개념 정리 (1) | 2020.11.08 |
[Spring-Boot] Spring-Boot Project A to Z (Lombok) (0) | 2020.10.31 |