Servlet&JSP
Step04_Final (file, 파일 업로드)
bono.html
2022. 8. 22. 16:08
파일 업로드를 한다면 업로드 되는 파일의 정보
1. 파일의 이름
2, 실제 파일 데이터 (byte 알갱이)
3. 파일의 크기
회원가입 정보 조회하는 법 (아이디, 비밀번호, 이메일)
직접 DB에 조회한다.
사용 파일 및 폴더
upload 파일을 자료는 저장할 파일이다. 이클립스에서 조회는 되지 않는다.
파일 업로드 게시판은 아래의 sql에 기반하여 사용된다.
list.jsp
<%@page import="test.file.dao.FileDao"%>
<%@page import="test.file.dto.FileDto"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//한 페이지에 몇개씩 표시할 것인지
final int PAGE_ROW_COUNT=5;
//하단 페이지를 몇개씩 표시할 것인지
final int PAGE_DISPLAY_COUNT=5;
//보여줄 페이지의 번호를 일단 1이라고 초기값 지정
int pageNum=1;
//페이지 번호가 파라미터로 전달되는지 읽어와 본다.
String strPageNum=request.getParameter("pageNum");
//만일 페이지 번호가 파라미터로 넘어 온다면
if(strPageNum != null){
//숫자로 바꿔서 보여줄 페이지 번호로 지정한다.
pageNum=Integer.parseInt(strPageNum);
}
//보여줄 페이지의 시작 ROWNUM
int startRowNum=1+(pageNum-1)*PAGE_ROW_COUNT;
//보여줄 페이지의 끝 ROWNUM
int endRowNum=pageNum*PAGE_ROW_COUNT;
//하단 시작 페이지 번호
int startPageNum = 1 + ((pageNum-1)/PAGE_DISPLAY_COUNT)*PAGE_DISPLAY_COUNT;
//하단 끝 페이지 번호
int endPageNum=startPageNum+PAGE_DISPLAY_COUNT-1;
//전체 글의 갯수
int totalRow=FileDao.getInstance().getCount();
//전체 페이지의 갯수 구하기
int totalPageCount=(int)Math.ceil(totalRow/(double)PAGE_ROW_COUNT);
//끝 페이지 번호가 이미 전체 페이지 갯수보다 크게 계산되었다면 잘못된 값이다.
if(endPageNum > totalPageCount){
endPageNum=totalPageCount; //보정해 준다.
}
//1. 파일 목록을 얻어와서
//FileDto 객체를 생성해서
FileDto dto=new FileDto();
//위에서 계산된 startRowNum , endRowNum 을 담아서
dto.setStartRowNum(startRowNum);
dto.setEndRowNum(endRowNum);
//파일 목록을 select 해 온다.
List<FileDto> list=FileDao.getInstance().getList(dto);
//2. 응답하기
//세션 영역의 id 를 읽어와 본다.
String id=(String)session.getAttribute("id");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/file/list.jsp</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<h1>자료실 목록 입니다.</h1>
<table class="table table-striped">
<thead class="table-dark">
<tr>
<th>번호</th>
<th>작성자</th>
<th>제목</th>
<th>파일명</th>
<th>크기</th>
<th>등록일</th>
<th>삭제</th>
</tr>
</thead>
<tbody>
<%for(FileDto tmp:list){ %>
<tr>
<td><%=tmp.getNum() %></td>
<td><%=tmp.getWriter() %></td>
<td><%=tmp.getTitle()%></td>
<td><a href="download.jsp?num=<%=tmp.getNum()%>"><%=tmp.getOrgFileName() %></a></td>
<td><%=tmp.getFileSize() %></td>
<td><%=tmp.getRegdate() %></td>
<td>
<%if(tmp.getWriter().equals(id)){ %>
<a href="delete.jsp?num=<%=tmp.getNum()%>">삭제</a>
<%} %>
</td>
</tr>
<%} %>
</tbody>
</table>
<nav>
<ul class="pagination">
<%if(startPageNum != 1){ %>
<li class="page-item">
<a class="page-link" href="list.jsp?pageNum=<%=startPageNum-1 %>">Prev</a>
</li>
<%} %>
<%for(int i=startPageNum; i<=endPageNum; i++){ %>
<%if(pageNum == i){ %>
<li class="page-item active">
<a class="page-link" href="list.jsp?pageNum=<%=i %>"><%=i %></a>
</li>
<%}else{ %>
<li class="page-item">
<a class="page-link" href="list.jsp?pageNum=<%=i %>"><%=i %></a>
</li>
<%} %>
<%} %>
<%if(endPageNum < totalPageCount){ %>
<li class="page-item">
<a class="page-link" href="list.jsp?pageNum=<%=endPageNum+1 %>">Next</a>
</li>
<%} %>
</ul>
</nav>
<a href="${pageContext.request.contextPath }/file/private/upload_form.jsp">업로드 하러 가기</a>
</div>
</body>
</html>
upload_form.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/file/private/upload_form.jsp</title>
</head>
<body>
<div class="container">
<h1>파일 업로드 폼 입니다.</h1>
<!--
파일 업로드 폼 작성법
1. method="post"
2. enctype="multipart/form-data"
3. <input type="file" />
- enctype="multipart/form-data" 가 설정된 폼을 전송하면
폼전송된 내용을 추출할때 HttpServletRequest 객체로 추출을 할수 없다
MultipartRequest 객체를 이용해서 추출해야 한다.
-->
<form action="upload.jsp" method="post" enctype="multipart/form-data">
<div>
<label for="title">제목</label>
<input type="text" name="title" id="title"/>
</div>
<div>
<label for="myFile">첨부파일</label>
<input type="file" name="myFile" id="myFile"/>
</div>
<button type="submit">업로드</button>
</form>
</div>
</body>
</html>
upload.jsp
<%@page import="test.file.dao.FileDao"%>
<%@page import="test.file.dto.FileDto"%>
<%@page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy"%>
<%@page import="com.oreilly.servlet.MultipartRequest"%>
<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
// /test/upload.jsp 파일을 참고해서 아래의 작업을 자알~~ 해 보세요
//1. 폼 전송되는 파일을 webapp/upload 폴더에 저장한다.
String realPath=application.getRealPath("/upload");
//해당 경로를 access 할수 있는 파일 객체 생성
File f=new File(realPath);
if(!f.exists()){ //만일 폴더가 존재 하지 않으면
f.mkdir(); //upload 폴더 만들기
}
MultipartRequest mr=new MultipartRequest(request,
realPath, //업로드된 파일을 저장할 경로
1024*1024*50, //최대 업로드 사이즈
"utf-8", //한글 파일명 깨지지 않도록
new DefaultFileRenamePolicy());
//2. 폼 전송되는 title 을 읽어온다.
String title=mr.getParameter("title");
//3. 파일의 작성자(업로더)는 HttpSession 객체에서 읽어온다.
String writer=(String)session.getAttribute("id");
//4. 추가로 원본파일명, 저장된파일명, 파일사이즈도 얻어내서 FileDto 객체에 담아서
String orgFileName=mr.getOriginalFileName("myFile");
String saveFileName=mr.getFilesystemName("myFile");
long fileSize=mr.getFile("myFile").length();
//업로드된 파일의 정보를 FileDto 에 담고
FileDto dto=new FileDto();
dto.setWriter(writer);
dto.setTitle(title);
dto.setOrgFileName(orgFileName);
dto.setSaveFileName(saveFileName);
dto.setFileSize(fileSize);
//5. FileDao 객체를 이용해서 DB 에 저장하고
boolean isSuccess=FileDao.getInstance().insert(dto);
//6. 응답한다.
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/file/private/upload.jsp</title>
</head>
<body>
<%if(isSuccess){ %>
<p>
<%=writer %> 님이 업로드한 <%=orgFileName %> 파일을 저장했습니다.
<a href="${pageContext.request.contextPath }/file/list.jsp">목록보기</a>
</p>
<%}else{ %>
<p>
업로드 실패!
<a href="upload_form.jsp">다시 시도</a>
</p>
<%} %>
</body>
</html>
파일 업로드는 단순히 파라미터 명을 가져오는 것으로는 부족하고 특별한 처리 방법이 필요하다.
파일 업로드 처리를 하기 위해서는 MultipartRequest 객체를 이용해야 한다.(cos.jar에서 import한 것)
new DefaultFileRenamePolicy()
파일명을 리네임해서 중복 파일명 오류를 막아준다.
delete.jsp
<%@page import="java.io.File"%>
<%@page import="test.file.dao.FileDao"%>
<%@page import="test.file.dto.FileDto"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//1. 삭제할 파일 번호를 읽어온다.
int num=Integer.parseInt(request.getParameter("num"));
//2. 해당 파일의 정보를 DB 에서 읽어온다.
FileDto dto=FileDao.getInstance().getData(num);
//3. 파일 시스템에서 삭제 한다. (webapp/upload 폴더에서 해당 파일을 삭제)
//삭제할 파일의 실제 경로 구성하기
String path=request.getServletContext().getRealPath("/upload")+
File.separator+dto.getSaveFileName();
File f=new File(path);
f.delete();
//4. DB 에서도 해당 파일의 정보를 삭제 한다.
FileDao.getInstance().delete(num);
//5. 응답하기 (리다일렉트)
String cPath=request.getContextPath();
response.sendRedirect(cPath+"/file/list.jsp"); //파일 목록보기로 다시 리다일렉트 이동 시킨다.
%>
download.jsp
<%@page import="java.io.BufferedOutputStream"%>
<%@page import="java.net.URLEncoder"%>
<%@page import="java.io.FileInputStream"%>
<%@page import="java.io.File"%>
<%@page import="test.file.dao.FileDao"%>
<%@page import="test.file.dto.FileDto"%>
<%@ page language="java" contentType="application/octet-stream; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//1. GET 방식 파라미터로 전달되는 다운로드 시켜줄 파일의 번호를 읽어온다.
int num=Integer.parseInt(request.getParameter("num"));
/*
2. DB 에서 다운로드 시켜줄 파일의 정보를 읽어온다.
어떤 파일명으로 upload 폴더에 저장되어 있는지 => saveFileName
업로드 당시 원본 파일명은 무엇인지 => orgFileName
파일의 사이는 어떻게 되는지 => fileSize
- 다운로드 시켜주기 위해서는 위의 3가지 정보가 필요하다.
*/
FileDto dto=FileDao.getInstance().getData(num);
//3. 서버의 파일시스템(upload) 에 저장된 파일에서 바이트 알갱이를 읽어서 출력한다(다운로드)
String orgFileName=dto.getOrgFileName();
String saveFileName=dto.getSaveFileName();
//다운로드 시켜줄 파일의 실제 경로 구성하기
String path=request.getServletContext().getRealPath("/upload")+
File.separator+saveFileName;
//다운로드할 파일에서 읽어들일 스트림 객체 생성하기
FileInputStream fis=new FileInputStream(path);
//다운로드 시켜주는 작업을 한다. (실제 파일 데이터와 원본파일명을 보내줘야한다.)
//다운로드 시켜주는 작업을 한다.
String encodedName=null;
//한글 파일명 세부처리
if(request.getHeader("User-Agent").contains("Firefox")){
//벤더사가 파이어 폭스인경우
encodedName=new String
(orgFileName.getBytes("utf-8"),"ISO-8859-1");
}else{ //그외 다른 벤더사
encodedName=URLEncoder.encode(orgFileName, "utf-8");
//파일명에 공백이있는 경우 처리
encodedName=encodedName.replaceAll("\\+"," ");
}
//응답 헤더 정보 설정
response.setHeader("Content-Disposition","attachment;filename="+encodedName);
response.setHeader("Content-Transfer-Encoding", "binary");
//다운로드할 파일의 크기 읽어와서 다운로드할 파일의 크기 설정
response.setContentLengthLong(dto.getFileSize());
//클라이언트에게 출력할수 있는 스트림 객체 얻어오기
BufferedOutputStream bos=
new BufferedOutputStream(response.getOutputStream());
//한번에 최대 1M byte 씩 읽어올수 있는 버퍼
byte[] buffer=new byte[1024*1024];
int readedByte=0;
//반복문 돌면서 출력해주기
while(true){
//byte[] 객체를 이용해서 파일에서 byte 알갱이 읽어오기
readedByte = fis.read(buffer);
if(readedByte == -1)break; //더이상 읽을 데이터가 없다면 반복문 빠져 나오기
//읽은 만큼 출력하기
bos.write(buffer, 0, readedByte);
bos.flush(); //출력
}
//FileInputStream 닫아주기
fis.close();
%>