Preface
이번 장에선 DB를 통해 파일을 업로드하고 웹 상에서 업로드한 파일을 다운로드하는 방법을 공부했다.
대부분의 내용은 쉽게 이해하고 넘어갈 수 있었지만,
파일 다운로드 파트의 IOStream과 User-Agent, CharacterSet 설정 부분이 다소 복잡했다.
뒷장의 MVC 패턴 게시판을 제작하며 코드를 한 번 더 작성해보면 익숙해지지 않을까 싶다.
- 라이브러리 다운로드: http://servlets.com/cos/
Servlets.com | com.oreilly.servlet
servlets.com
1. 파일 업로드
- 파일 업로드용 작성 폼을 제작할 때 주의할 사항
1) method의 속성을 반드시 post로 지정해야 한다.
2) enctype의 속성을 multipart/form-data로 지정해야 한다.
3) <input> 태그의 type 속성을 file로 지정해야 한다.
<form method="post" enctype="multipart/form-data" action="업로드 처리 파일 경로">
<input type="file" name="input 이름" />
</form>
- enctype: 폼값을 서버로 전송할 때의 인코딩 방식을 지정하는 속성
속성값 | 설명 |
application/x-www-form-urlencoded | 모든 문자를 서버로 전송하기 전에 인코딩한다. (기본값) |
multipart/form-data | 모든 문자를 인코딩하지 않는다. <form> 태그를 통해 파일을 서버로 전송할 때 주로 사용한다. |
text/plain | 공백 문자(space)만 "+" 기호로 변환하고, 나머지는 인코딩하지 않는다. |
- 업로드용 폼 화면
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<script>
function validateForm(form) {
if (form.name.value == "") {
alert("작성자를 입력하세요.");
form.name.focus();
return false;
}
if (form.title.value == "") {
alert("제목을 입력하세요.");
form.title.focus();
return false;
}
if (form.attachedFile.value == "") {
alert("첨부파일은 필수 입력입니다.");
return false;
}
}
</script>
<body>
<h3>파일 업로드</h3>
<span style="color: red;">${ errorMessage }</span>
<form name="fileForm" method="post" enctype="multipart/form-data"
action="UploadProcess.jsp" onsubmit="return validateForm(this);">
작성자: <input type="text" name="name" value="머스트해브" /><br> 제목: <input
type="text" name="title" /><br> 카테고리(선택사항): <input
type="checkbox" name="cate" value="사진" checked />사진 <input
type="checkbox" name="cate" value="과제" />과제 <input
type="checkbox" name="cate" value="워드" />워드 <input
type="checkbox" name="cate" value="음원" />음원 <br>
첨부파일: <input type="file" name="attachedFile" /><br> <input
type="submit" value="전송하기" />
</form>
</body>
</html>
- 파일 테이블
create table myfile (
idx number primary key,
name varchar2(50) not null,
title varchar2(200) not null,
cate varchar2(100),
ofile varchar2(100) not null,
sfile varchar2(30) not null,
postdate date default sysdate not null
);
- DTO 클래스
package fileupload;
public class MyfileDTO {
private String idx;
private String name;
private String title;
private String cate;
private String ofile;
private String sfile;
private String postdate;
public String getIdx() {
return idx;
}
public void setIdx(String idx) {
this.idx = idx;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getCate() {
return cate;
}
public void setCate(String cate) {
this.cate = cate;
}
public String getOfile() {
return ofile;
}
public void setOfile(String ofile) {
this.ofile = ofile;
}
public String getSfile() {
return sfile;
}
public void setSfile(String sfile) {
this.sfile = sfile;
}
public String getPostdate() {
return postdate;
}
public void setPostdate(String postdate) {
this.postdate = postdate;
}
}
- DAO 클래스
package fileupload;
import java.util.*;
import common.DBConnPool;
public class MyfileDAO extends DBConnPool {
public int insertFile(MyfileDTO dto) {
int applyResult = 0;
try {
String query = "INSERT INTO myfile ( " + " idx, name, title, cate, ofile, sfile) " + " VALUES ( "
+ " seq_board_num.nextval, ?, ?, ?, ?, ?)";
psmt = con.prepareStatement(query);
psmt.setString(1, dto.getName());
psmt.setString(2, dto.getTitle());
psmt.setString(3, dto.getCate());
psmt.setString(4, dto.getOfile());
psmt.setString(5, dto.getSfile());
applyResult = psmt.executeUpdate();
} catch (Exception e) {
System.out.println("INSERT 증 예외 발생");
e.printStackTrace();
}
return applyResult;
}
public List<MyfileDTO> myFileList() {
List<MyfileDTO> fileList = new Vector<MyfileDTO>();
String query = "SELECT * FROM myfile ORDER BY idx DESC";
try {
psmt = con.prepareStatement(query);
rs = psmt.executeQuery();
while (rs.next()) {
MyfileDTO dto = new MyfileDTO();
dto.setIdx(rs.getString(1));
dto.setName(rs.getString(2));
dto.setTitle(rs.getString(3));
dto.setCate(rs.getString(4));
dto.setOfile(rs.getString(5));
dto.setSfile(rs.getString(6));
dto.setPostdate(rs.getString(7));
fileList.add(dto);
}
} catch (Exception e) {
System.out.println("SELECT 시 예외 발생");
e.printStackTrace();
}
return fileList;
}
}
- 이번 글 제일 상단에서 추가한 확장 라이브러리에는 MultipartRequest라는 클래스가 있다.
1) enctype을 multipart/form-data로 지정하면 request의 getParameter( ) 대신 MultipartRequest 클래스를 사용해야 한다.
2) 객체 생성과 동시에 파일이 업로드되며, 객체 생성 실패 시 파일은 업로드되지 않는다.
- 파일 업로드 처리 순서
1) MultipartRequest 객체 새성
2) 새로운 파일명 생성
3) 파일명 변경
→ 웹 서버의 인코딩 방식에 따라 한글이 깨질 수도 있기 때문이며, 파일명을 영문과 숫자의 조합으로 설정하는 것이 좋다.
4) 나머지 폼값 처리
5) DTO 생성
6) DAO를 통해 DB에 반영
7) 파일 목록 JSP로 redirect 처리
<%@ page import="fileupload.MyfileDAO"%>
<%@ page import="fileupload.MyfileDTO"%>
<%@ page import="java.util.Date"%>
<%@ page import="java.text.SimpleDateFormat"%>
<%@ page import="java.io.File"%>
<%@ page import="com.oreilly.servlet.MultipartRequest"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String saveDirectory = application.getRealPath("/Uploads");
int maxPostSize = 1024 * 1000; /* 1MB */
String encoding = "UTF-8";
try {
MultipartRequest mr = new MultipartRequest(request, saveDirectory, maxPostSize, encoding);
String fileName = mr.getFilesystemName("attachedFile");
String ext = fileName.substring(fileName.lastIndexOf(".")); /* 파일 확장자 */
String now = new SimpleDateFormat("yyyyMMdd_HmsS").format(new Date());
String newFileName = now + ext;
File oldFile = new File(saveDirectory + File.separator + fileName);
File newFile = new File(saveDirectory + File.separator + newFileName);
oldFile.renameTo(newFile);
String name = mr.getParameter("name");
String title = mr.getParameter("title");
String[] cateArray = mr.getParameterValues("cate");
StringBuffer cateBuf = new StringBuffer();
if (cateArray == null) {
cateBuf.append("선택 없음");
} else {
for (String s : cateArray) {
cateBuf.append(s + ", ");
}
}
MyfileDTO dto = new MyfileDTO();
dto.setName(name);
dto.setTitle(title);
dto.setCate(cateBuf.toString());
dto.setOfile(fileName);
dto.setSfile(newFileName);
MyfileDAO dao = new MyfileDAO();
dao.insertFile(dto);
dao.close();
response.sendRedirect("FileList.jsp");
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("errorMessage", "파일 업로드 오류");
request.getRequestDispatcher("FileUploadMain.jsp").forward(request, response);
}
%>
2. 파일 목록 보기
- 파일 목록 출력
<%@ page import="java.net.URLEncoder"%>
<%@ page import="java.util.List"%>
<%@ page import="fileupload.MyfileDAO"%>
<%@ page import="fileupload.MyfileDTO"%>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>DB에 등록된 파일 목록 보기</h2>
<%
String nowDate = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(new Date());
%>
현재 시간: <%= nowDate %><br>
<a href="FileUploadMain.jsp">파일 등록하기</a>
<%
MyfileDAO dao = new MyfileDAO();
List<MyfileDTO> fileLists = dao.myFileList();
dao.close();
%>
<table border="1">
<tr>
<th>No</th>
<th>작성</th>
<th>제목</th>
<th>카테고리</th>
<th>원본 파일명</th>
<th>저장된 파일명</th>
<th>작성일</th>
<th></th>
</tr>
<% for(MyfileDTO f : fileLists) { %>
<tr>
<td><%= f.getIdx() %></td>
<td><%= f.getName() %></td>
<td><%= f.getTitle() %></td>
<td><%= f.getCate() %></td>
<td><%= f.getOfile() %></td>
<td><%= f.getSfile() %></td>
<td><%= f.getPostdate() %></td>
<td><a
href="Download.jsp?oName=<%= URLEncoder.encode(f.getOfile(),
"UTF-8") %>&sName=<%= URLEncoder.encode(f.getSfile(), "UTF-8") %>">[다운로드]</a></td>
</tr>
<% } %>
</table>
</body>
</html>
3. 파일 다운로드
- 파일 다운로드
<%@ page import="utils.JSFunction"%>
<%@ page import="java.io.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String saveDirectory = application.getRealPath("/Uploads");
String saveFilename = request.getParameter("sName");
String originalFilename = request.getParameter("oName");
try {
File file = new File(saveDirectory, saveFilename);
InputStream inStream = new FileInputStream(file);
String client = request.getHeader("User-Agent");
if (client.indexOf("WOW64") == -1) {
originalFilename = new String(originalFilename.getBytes("UTF-8"), "ISO-8859-1");
} else {
originalFilename = new String(originalFilename.getBytes("KSC5601"), "ISO-8859-1");
}
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + originalFilename + "\"");
response.setHeader("Content-Length", "" + file.length());
out.clear();
OutputStream outStream = response.getOutputStream();
byte[] b = new byte[(int) file.length()];
int readBuffer = 0;
while ((readBuffer = inStream.read(b)) > 0) {
outStream.write(b, 0, readBuffer);
}
inStream.close();
outStream.close();
} catch (FileNotFoundException e) {
JSFunction.alertBack("파일을 찾을 수 없습니다.", out);
} catch (Exception e) {
JSFunction.alertBack("예외가 발생하였습니다.", out);
}
%>
- User-Agent를 통해 웹 브라우저의 종류를 알 수 있다.
- octet-stream: 8비트 단위의 바이너리 데이터
- 파일명에 특수기호가 포함되어 있을 경우 발생하는 오류를 해결하려면 server.xml에 다음 내용을 추가하면 된다.
<Connector connectionTimeout="20000" port="8081" protocol="HTTP/1.1"
redirectPort="8443" relaxedQueryChars="[]()^|"" />
'JSP > 성낙현의 JSP 자바 웹 프로그래밍' 카테고리의 다른 글
JSP, Oracle을 사용한 자료실형 게시판(MVC 패턴) (0) | 2023.06.29 |
---|---|
서블릿(Servlet) (0) | 2023.06.26 |
JSP 표준 태그 라이브러리(JSTL) (0) | 2023.06.21 |
표현 언어(EL: Expression Language) (0) | 2023.06.19 |
JSP, Oracle을 사용한 회원제 게시판(페이징 O) (0) | 2023.06.19 |
댓글