파일 업로드 라이브러리 설치
- JSP에서 파일 업로드 기능을 구현하기 위해서 오픈 라이브러리를 사용하면 쉽게 구현할 수 있다.
- 파일 업로드 라이브러리에는 대표적으로 cos.jar와 commons-fileupload.jar가 있다.
- 여기서는 cos.jar를 사용하도록 하겠다.
cos.jar 다운로드 사이트
Servlets.com | com.oreilly.servlet
Servlets.com | com.oreilly.servlet
www.servlets.com
- cos-22.05.zip파일을 다운로드 받아서 압축 풀기
- cos-22.05 안에 cos.jar 파일을 복사하기
- "컨텍스트(MyMVC)/webapp/WEB-INF/lib" 안에 붙여넣기
파일 업로드시 주의사항
- form태그의 method를 post로 지정
- form태그의 enctype을 multipart/form-data로 지정
※ 디폴트값인 application/x-www-form-urlencoded로 지정하면 파일명만 전송된다.
[View] input.jsp 수정 (게시판 글쓰기 페이지)
[경로: src/main/webapp/board/input.jsp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<div class="container">
<h1>MVC Board</h1>
<br><br>
<form name="bbsF" method="post" action="insert.do" enctype="multipart/form-data">
<table border="1">
...
<tr>
<th>첨부파일</th>
<td>
<input type="file" name="fileName" id="fileName">
</td>
</tr>
</table>
</form>
</div>
|
cs |
[Controller] BoardInsertAction 수정
[경로: src/main/java/board.controller/BoardInsertAction.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
58
59
60
61
62
63
64
65
66
67
|
public class BoardInsertAction extends AbstractAction {
@Override
public void execute(HttpServletRequest req, HttpServletResponse res) throws Exception {
//0. 파일 업로드 처리
//<1> 업로드할 디렉토리의 절대경로 얻기 (src/main/webapp/upload 파일)
ServletContext application = req.getServletContext();
System.out.println("application: " + application);
//업로드된 파일의 실제 절대경로 구하기
String upDir = application.getRealPath("/upload");
System.out.println("upDir: " + upDir);
//<2> cos.jar의 MultipartRequest 객체 생성 ==> 업로드 처리를 해줌
MultipartRequest mreq = null;
try {
DefaultFileRenamePolicy df = new DefaultFileRenamePolicy();
//FileRenamePolicy: 동일한 파일명의 파일이 올라왔을 때 덮어쓰지 않게 방지함 (파일명에 인덱스 번호를 붙인다.)
mreq = new MultipartRequest(req, upDir, 1024*1024*100, "utf-8", df);
//(HttpServletRequest request, 저장할 경로, 업로드 최대용량(100MB), 인코딩 방식(한글처리), policy);
}catch (IOException e) {//용량 초과시 IOException 발생
System.out.println("파일 업로드 실패: " + e);
return;
}
//1. 사용자가 입력한 값 받기(MultipartRequest를 이용해 파라미터값 추출)
String title = mreq.getParameter("title");
String name = mreq.getParameter("name");
String content = mreq.getParameter("content");
String passwd = mreq.getParameter("passwd");
//첨부파일명 받기
String fileName = mreq.getFilesystemName("fileName");
//첨부파일명: getParameter(X) => getFileSystemName("파라미터명)
//파일크기 받기
File file = mreq.getFile("fileName");
long fileSize = 0;
if(file!=null) {
fileSize = file.length(); //파일크기
}
//2. 유효성 체크
if(title==null || name==null || passwd==null || title.trim().isBlank()
|| name.trim().isBlank() || passwd.trim().isBlank()) {
this.setRedirect(true); //redirect이동
this.setViewName("input.do");//input.do로 redirect이동
return;
}
//3. 1번에서 받은 값 BoardVO객체에 담기
BoardVO vo = new BoardVO(0, name, passwd, title, content, null, 0, fileName, fileSize);
//4. BoardDAO 생성 후 insertBoard() 호출
BoardDAO dao = new BoardDAO();
int n = dao.insertBoard(vo);
//5. 그 결과 메시지, 이동경로 설정해서 req에 저장하기
String msg = (n>0)? "글 등록 완료":"글 등록 실패";
String loc = (n>0)? "list.do":"javascript:history.back()";
//request에 msg와 loc 저장 => message.jsp에서 꺼내야 함
req.setAttribute("msg", msg);
req.setAttribute("loc", loc);
//뷰페이지 지정
this.setViewName("/board/message.jsp");
//이동방식 지정
this.setRedirect(false); //forward이동
}
}
|
cs |
- pageContext 내장 객체의 메서드
- ServletContext getServletContext(): 페이지에 대한 서블릿 실행 환경 정보를 담고 있는 application 내장 객체를 리턴한다.
- 이클립스에서 upload 파일 경로: src/main/webapp/upload
- getRealPath("/"): src/main/webapp까지를 의미
- getReatPath("/upload"): src/main/webapp/upload를 의미
- getRealPath: upload 파일의 실제 위치는 훨씬 깊숙한 곳에 있는데, 그 절대 경로를 알아내는 메서드
- 실제 위치(upDir): C:\multicampus\Java-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\MyMVC\upload
- MultipartRequest mreq = new MultipartRequest(HttpServletRequest request, 저장할 경로, 업로드 최대용량, 인코딩 방식, policy)
- FileRenamePolicy: 동일한 파일명의 파일이 올라오면 디폴트로는 새로운 파일이 예전 파일을 덮어쓰면서 예전 파일이 사라진다. 이를 방지하기 위해 FileRenamePolicy를 이용하면 동일한 파일명의 파일이 올라왔을 때 덮어쓰지 않게 방지한다.
[View] view.jsp 수정 (코드 추가)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<tr>
<td width="20%" class="m1"><b>첨부파일</b></td>
<td colspan="3" class="text-left">
<!-- 첨부파일이 있다면 -->
<c:if test="${vo.fileName ne null}">
<a href="${pageContext.request.contextPath}/upload/${vo.fileName}" download>${vo.fileName}</a>
<%-- request.getContextPath()와 동일
${pageContext.request.contextPath}: 내 현재 위치 -> MyMVC 반환
--%>
[${vo.fileSize}]bytes
</c:if>
</td>
</tr>
|
cs |
게시판 글목록 수정
첨부파일이 있는 글에 첨부파일 아이콘이 보이게 설정해보자.
아이콘 다운로드
[경로: src/main/webapp/images/attach.png]
[View] list.jsp 수정 (코드 추가)
1
2
3
4
5
6
|
<td>
<a href="view.do?num=${board.num}">${board.title}</a>
<c:if test="${board.fileName ne null}">
<img src="../images/attach.png" style="width:0.8em">
</c:if>
</td>
|
cs |
글삭제 수정
첨부파일이 있는 글을 삭제하면 첨부파일도 같이 삭제되도록 설정
[Controller] BoardDeleteAction 수정 (코드 추가)
1
2
3
4
5
6
7
8
9
10
11
12
|
//5. 서버에 업로드했던 파일이 있다면 서버에서 삭제 처리
String fileName = tmp.getFileName();//첨부파일명
if(fileName!=null) {
//절대경로(업로드 디렉토리)
String upDir = req.getServletContext().getRealPath("/upload");
File delFile = new File(upDir, fileName); //(상위 디렉터리, 파일명)
if(delFile.exists()) {
boolean b = delFile.delete();
System.out.println("파일 삭제 여부: " + b);
}
}
|
cs |
C:\multicampus\Java-workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\MyMVC\upload에 들어가서 확인해보면 첨부파일이 삭제된 것을 확인할 수 있다.
글수정 수정
첨부파일도 수정할 수 있도록 설정
[View] edit.jsp 수정 (코드 추가)
[경로: src/main/webapp/board/edit.jsp]
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<tr>
<th>첨부파일</th>
<td>
<c:if test="${board.fileName ne null}">
${board.fileName} [${board.fileSize} bytes]
<br>
</c:if>
<!-- 옛 첨부파일을 hidden으로 넘기자 ------------------- -->
<input type="hidden" name="old_fileName" value="${board.fileName}">
<!-- -------------------------------------------- -->
<input type="file" name="fileName" id="fileName">
</td>
</tr>
|
cs |
[Controller] BoardUpdateAction 수정
[경로: src/main/java/board.controller/BoardUpdateAction.java]
주석 0번, 4-2번 추가
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
58
59
60
|
public class BoardUpdateAction extends AbstractAction {
@Override
public void execute(HttpServletRequest req, HttpServletResponse res) throws Exception {
//0. 파일 업로드 처리
String upDir = req.getServletContext().getRealPath("/upload");
System.out.println("upDir확인: " + upDir);
MultipartRequest mreq = new MultipartRequest(req, upDir, 100*1024*1024,
"utf-8", new DefaultFileRenamePolicy());
//1. 글번호, 제목, 작성자, 내용, 비밀번호, 첨부파일 받기
String numStr = mreq.getParameter("num");
String title = mreq.getParameter("title");
String name = mreq.getParameter("name");
String content = mreq.getParameter("content");
String passwd = mreq.getParameter("passwd");
String old_fileName = mreq.getParameter("old_fileName"); //예전 첨부파일명
//첨부파일
String fileName = mreq.getFilesystemName("fileName"); //새로 첨부하는 파일명
long fileSize = 0;
File file = mreq.getFile("fileName");
if(file!=null) {
fileSize = file.length();
}
//2. 유효성 체크
if(numStr==null|| title==null || name==null || passwd==null || numStr.trim().isBlank()
|| title.trim().isBlank() || name.trim().isBlank() || passwd.trim().isBlank()) {
this.setRedirect(true); //redirect이동
this.setViewName("update.do"); //update.do로 redirect이동
return;
}
int num = Integer.parseInt(numStr.trim());
//3. 1번에서 받은 값을 BoardVO에 담기
BoardVO vo = new BoardVO(num, name, passwd, title, content, null, 0, fileName, fileSize);
//4. BoardDAO의 updateBoard(vo) 호출
BoardDAO dao = new BoardDAO();
int n = dao.updateBoard(vo);
//4_2. 새로운 파일을 업로드했다면 예전에 업로드했던 파일은 서버에서 지우자
if(fileName!=null && old_fileName!=null) {
File delFile = new File(upDir, old_fileName);
if(delFile.exists()) {
boolean b = delFile.delete();
System.out.println("옛파일 삭제 여부: " + b);
}
}
//5. 그 결과 메시지, 이동경로 처리
String msg = (n>0)? "글수정 성공":"글수정 실패";
String loc = (n>0)? "list.do":"javascript:history.back()";
req.setAttribute("msg", msg);
req.setAttribute("loc", loc);
this.setViewName("/board/message.jsp");
this.setRedirect(false);
}
}
|
cs |