티스토리 뷰

반응형


| File Upload



Spring framework에 File Upload 기능을 구현해보자.



|| Setting



Spring framework에 File Upload 기능을 구현하기 위해

Library를 추가해주자.

Apache Commons FileUpload


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(45DEFAULT NULL,
  `saveFolder` varchar(45DEFAULT NULL,
  `originFile` varchar(45DEFAULT NULL,
  `saveFile` varchar(45DEFAULT 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(16NOT NULL,
  `subject` varchar(100NOT NULL,
  `content` varchar(2000NOT 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





반응형
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday