웹 서버 만들기(13) 업로드 다운로드 조회수
파일 업로드
- Part API를 이용하여 파일 업로드를 구현하였다.
단일 파일 최대 용량 설정 (Nginx)
/etc/nginx/nginx.conf
에서 단일 파일 최대 용량을 500MB로 설정하였다.
... 생략 ...
http {
client_max_body_size 500M;
... 생략...
}
- 업로드 할 단일 파일 크기가 500MB를 넘으면 오류가 발생한다.
게시글 새로 저장
JSP
설명
- file 선택을 위한
input
태그에서 type 속성 값을file
로 설정한다. form
태그에서 enctype 속성 값을multipart/form-data
로 해준다. 대용량 파일을 서버로 보내기 위한 설정이다.- 한 게시물당 최대 3개 까지 첨부파일을 저장할 수 있지만 아직 한 게시물에 동시에 파일 추가되는 기능은 구현하지 않았다.
과정
- 사용자가 글쓰기 버튼을 클릭한다.
- 제목과 본문을 작성한다. 그리고 파일선택 버튼을 클릭하여 업로드할 파일을 선택하여준다.
- 게시글 작성이 완료되고 저장 버튼을 누르면 게시글과 첨부파일이 서버로 전송되어 저장된다.
- 게시글 확인
- 서버에 파일이 잘 업로드 된 것을 확인할 수 있다.
코드 ( notice_write.jsp )
- form 의 enctype 속성을 추가하였고, input의 type 속성을
file
로 해주었다.
... 생략 ...
<form method="post" action="${noticeBoardURL}" enctype="multipart/form-data">
... 생략 ...
<input type="file" name="UPLOAD_FILE"/>
... 생략 ...
Servlet
설명
- 사용자가 전송한 제목, 본문은 DB서버에 저장하고 파일은 웹 서버에 저장한다.
- POST 요청으로 들어온다.
- 최대 3개의 파일 까지 서버에 저장이 가능하다.
part.getHeader("Content-Disposition").contains("filename=")
을 통해 파일인지 아니면 그냥 텍스트인지 구분한다.- 파일이름이 없으면 서버에 파일을 저장하지 않는다.
과정
- 사용자가 제목, 본문 그리고 파일을 첨부하여 POST 전송으로 서버에 보낸다.
- 제목과 본문을 DB서버에 저장해준다.
- 게시물 저장 시간과 게시물 번호를 이용하여 게시문 파일을 저장하기 위한 폴더를 만들어준다.
- 폴더에 사용자가 첨부한 파일을 저장한다. 최대 저장가능한 파일 수를 3개로 설정하였다.
- 하지만 만약 파일을 서버에 저장 중 오류가 발생한다면 게시글 즉 제목과 본문은 저장되고 파일만 서버에 저장이 안된다.
코드 ( MainPage.java )
- 사용자가 업로드한 파일을 웹 서버에 바로 저장해 준다.
private final int MAX_FILE_NUM = 2; // 최대 저장 가능한 파일수-1
public class MainPage extends HttpServlet{
... 생략 ...
private boolean uriSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 글 저장
if(request.getMethod().equals("POST") && uri.equals(noticeBoardSave)){
var charSet = "utf-8";
request.setCharacterEncoding(charSet);
if(!request.getContentType().toLowerCase().startsWith("multipart/form-data")){
//404 에러 발생
// application/x-www-form-urlencoded 타입이 아니라.
// 오직 multipart/form-data 타입만 받는다.
return false;
}
... 생략 ...
// 제목 본문 DB에 저장.
var user = (User)request.getAttribute("user");
var notice = new NoticeBoardDAO(getServletContext());
var genTime = System.currentTimeMillis(); // 게시글 생성 시각
var noticeSid = notice.saveNotice(user, title, mainText,genTime);
if(noticeSid == 0){ // db 저장 실패
result = "<script>alert('fail');location.href='"+noticeBoardWrite+"'</script>";
simplePage(response, result);
return true;
}
// ***** 파일 업로드 시작 *****
var noticeRe = "no file";
try{
// 폴더 생성 또는 존재 확인
// 저장을 위한 폴더
var folderName = genTime+"_"+ noticeSid;
var uploadPath = getFilePath(request, folderName);
// genTime + _ + noticeSID => 폴더명
var parts = request.getParts();
for(var part:parts){
if(!part.getHeader("Content-Disposition").contains("filename=")){
continue;
}
if(part.getSubmittedFileName().equals("")){
continue;
}
var file = new File(uploadPath);
if(!file.exists()){
// 폴더 생성
file.mkdir();
System.out.println(noticeSid+"게시글 폴더 생성 함");
}
var count = file.listFiles().length;
if(count > MAX_FILE_NUM){
System.out.println("최대 파일 갯수 초과");
break;
}
System.out.println("퐅더 존재? => "+file.isDirectory());
System.out.println("타입 : "+part.getHeader("Content-Disposition"));
System.out.println("크기 : "+part.getSize());
System.out.println("이름 : "+part.getSubmittedFileName());
part.write(uploadPath+File.separator+part.getSubmittedFileName());
part.delete(); // 임시파일 삭제
System.out.println("업로드 성공");
noticeRe = "file upload success";
System.out.println("경로 : "+uploadPath);
}
}catch(Exception e){
e.printStackTrace();
System.out.println("파일 업로드 실패");
noticeRe = "file upload fail";
}
// ***** 파일 업로드 end *****
result = "<script>alert('success "+noticeRe+"');location.href='"+noticeBoard+"?page=1&q='</script>";
simplePage(response, result);
return true;
}
... 생략 ...
선택
선택 1. web.xml
request.getParts()
를 사용하기 위해서는 임시저장 폴더를 만들어 줘야 한다.file-size-threshold
에 설정한 용량이 넘으면 임시저장 폴더에 저장한다는 것이다.- 코드에서 실제 파일이 어디에 저장될 것인지 코딩해 줘야한다.
<servlet>
<servlet-name>mainpage</servlet-name>
<servlet-class>com.mingyu2.happyhackingmain.MainPage</servlet-class>
<multipart-config>
<location>/home/mq/temp</location>
<max-file-size>-1</max-file-size>
<max-request-size>-1</max-request-size>
<file-size-threshold>1024</file-size-threshold>
</multipart-config>
</servlet>
선택 2. annotation 적기.
- 각 클래스마다 적어주어야 한다.
- location 설정을 따로 안하면 자바가 지정해놓은 임시 저장소를 이용한다.
@MultipartConfig(
location = "/home/mq/temp",
maxFileSize = -1,
maxRequestSize = -1,
fileSizeThreshold = 1024
)
public class MainPage extends HttpServlet{
게시글 수정 및 저장 & 파일 삭제
JSP
설명
- 게시글 수정 상태에서 첨부파일을 다운 받을 수 있는 링크를 추가하였다.
- 첨부파일 삭제 버튼을 추가하였다. 삭제 버튼을 누르면 첨부파일이 바로 삭제된다.
fetch
를 이용하여 get 전송을 통해 첨부파일을 삭제하여 준다.- 새로운 첨부파일을 추가하고 저장버튼을 누르면 파일은 서버에 저장된다. 하지만 한 게시글 당 파일은 최대 3개까지 저장이 가능하다.
- 수정 모드 이면서 첨부 파일이 있으면 첨부파일 링크 및 파일 삭제버튼이 나타난다.
과정
- 자신이 쓴 게시글에서 수정 버튼을 클릭하여 게시글 수정 페이지로 들어간다.
- 수정 페이지에서 제목과 본문을 변경할 수 있다.
- 전에 올렸던 첨부파일 확인이 가능하다. 그리고 이 첨부파일을 삭제버튼을 통해 삭제도 가능하다.
- 새로운 파일을 추가할 수 있는
파일 버튼
도 존재한다. - 수정이 완료되면
저장
버튼을 눌러준다. - 만약 파일 첨부개수가 3개가 넘으면 새로운 파일은 추가되지 않는다. 하지만 변화된 게시글은 저장이 된다.
첨부파일 삭제 과정
- 첨부파일의 삭제 버튼을 눌러준다.
- javacript 코드의 fetch를 통해 첨부파일 삭제 get 요청이 보내진다.
- 첨부파일이 서버에서 정상적으로 삭제가 되면 json 형태로
success
응답을 받는다. - 성공적으로 삭제가 되면 첨부파일 링크를 테이블에서 삭제한다.
코드 ( notice_write.jsp )
- servlet 에서 파일이름 목록과 모드 정보를 받아온다.
<%@ page import="com.mingyu2.happyhackingmain.Pair" %>
<%@ page import="java.util.ArrayList" %>
<%
var mode = (Boolean)request.getAttribute("modifyMode");
if(mode == null){
mode = false;
}
var fileNameList = (ArrayList<Pair<String,String>>)request.getAttribute("fileNameList");
%>
- 수정 모드 & 첨부파일이 하나 이상이면 다운 링크 및 삭제 버튼을 표시해 준다.
<% if(mode && fileNameList.size() > 0) { %>
<% for(var file : fileNameList ) { %>
<tr id='<%=file.getD1() %>'>
<td colspan="2">
<a href="${noticeBoardFileDownload}?downlink=<%=file.getD2() %>"><%=file.getD1() %></a>
<a href="javascript:delete_file('<%=file.getD1() %>')">delete</a>
</td>
</tr>
<% } %>
<% }%>
<tr>
<td colspan="2">
<input type="file" name="UPLOAD_FILE"/>
</td>
</tr>
- 첨부파일 삭제 버튼을 누르면
delete_file
함수가 동작한다. - 파라미터로 첨부파일 이름을 가져온다.
<% if(mode) { %>
<% if(fileNameList.size() > 0) { %>
<script>
function delete_file(name){
var url = "${noticeBoardFileDelete}?noticeID=${noticeID}&filename="+name;
var re = confirm("삭제 하시겠습니까?");
if(!re){
return;
}
fetch(url)
.then((response)=> response.json())
.then((json)=>{
var j = JSON.stringify(json);
var re = JSON.parse(j).result;
if(re == "success"){
var table = document.getElementsByClassName("notice_write")[0];
for(var i = 0;i<table.rows.length;i++){
if(table.rows[i].id == name){
table.deleteRow(i);
break;
}
}
alert("파일 삭제 성공");
}else{
alert("파일 삭제 실패");
}
console.log(re);
});
}
</script>
<% } %>
<% } %>
- JSON.stringify(xx) : 자바 스크립트 객체를 Json data로 변환한다.
- JSON.parse(xx) : Json data를 자바 스크립트 객체로 변환한다.
Servlet
설명
- 자기가 쓴 게시글에서 삭제하기 버튼을 누르면 DB에 저장된 게시글이 삭제되고, 웹 서버에 저장된 첨부파일도 삭제된다.
- 사용자가 전송한 수정된 게시글을 DB서버에 저장한다.
- 사용자가 파일 삭제 요청을 하면 웹 서버에 저장된 첨부파일을 삭제한다.
과정
- 사용자가 게시글을 수정 및 파일을 추가 하여 서버에 수정본 저장 요청을 보낸다.
- 서버는 옛 게시글을 삭제한다.
- 서버는 수정된 제목과 본문을 DB 서버에 새롭게 저장한다.
- 옛 게시글의 첨부파일이 들어있는 폴더 명을 새로운 게시글과 연관된 폴더명으로 변경해 준다. ( 파일명 : 게시글생성일_게시글번호 )
- 새로운 첨부파일이 있으면 웹 서버의 게시글 관련 폴더에 저장해 준다.
- 새로운 개시글과 첨부파일 저장이 완료되면 사용자에게 성공 응답을 한다.
첨부파일 삭제과정
- 사용자는 javascript의 fetch 함수를 통해 첨부파일 삭제 get 요청을 보낸다.
- 요청을 보낼 때 파라미터로 게시글 번호와 첨부파일 명을 보내준다.
- 서버에서 게시글 번호와 첨부파일 명을 받아 게시글 작성자와 요청을 보낸 사용자가 일치하는지 확인한다.
- 일치하면 존재하는 첨부파일인지 확인한다.
- 파일이 존재하면 저장된 파일을 삭제한다.
- json 형태로 사용자에게 파일 삭제 성공 하였다고 응답한다. (success)
게시글 삭제 과정
- 사용자는 자기가 쓴 글 삭제요청을 보낸다.
- DB에 저장된 게시글을 삭제한다.
- 파일 업로드 폴더를 삭제한다.
- 삭제완료 응답을 사용자에게 보낸다.
코드 (MainPage.java)
- 게시글 삭제
- 업로드 한 파일을 저장하기 위한 폴더를 삭제해 준다.
public class MainPage extends HttpServlet{
... 생략 ...
private boolean uriSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 글 삭제하기
if(request.getMethod().equals("GET") && uri.equals(noticeBoardDelete)){
try {
... 생략 ...
// *** 파일 업로드 폴더 삭제 ***
var folderName = notice.getGenTime()+"_"+notice.getSid();
var uploadPath = getFilePath(request, folderName);
var folder = new File(uploadPath);
if(folder.exists()){
var files = folder.listFiles();
// 모든 파일 삭제
for(var file : files){
file.delete();
}
// 마지막으로 폴더 삭제
folder.delete();
}
// *** 파일 업로드 폴더 삭제 end ***
result = "<script>alert('success');location.href='"+noticeBoard+"'</script>";
simplePage(response, result);
return true;
} catch (Exception e) {
e.printStackTrace();
}
}
- 첨부파일 삭제
- 일치하는 파일 하나만을 삭제한다.
public class MainPage extends HttpServlet{
... 생략 ...
private boolean uriSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 파일 삭제
if(request.getMethod().equals("GET") && uri.equals(noticeBoardFileDelete)){
var re = "fail";
try{
var noticeID = request.getParameter("noticeID");
var filename = request.getParameter("filename");
var sid = Long.parseLong(noticeID);
var noticeDAO = new NoticeBoardDAO(getServletContext());
var notice = noticeDAO.getNotice(sid);
if(notice == null) {
System.out.println("존재하지 않는 게시글");
simplePage(response, "{\"result\":\"fail\"}");
return true;
}
var user = (User)request.getAttribute("user");
if(user.getSid() != notice.getUserSID()){ // 글 작성자와 현재 유저와 일치안함 실패
System.out.println("글 작성자와 현재유저 일치 안함");
simplePage(response, "{\"result\":\"fail\"}");
return true;
}
// 파일 존재 여부 확인
var folderName = notice.getGenTime()+"_"+notice.getSid();
var filePath = getFilePath(request, folderName+File.separatorChar+filename);
var file = new File(filePath);
if(!file.exists()){
System.out.println("파일 존재 안함!");
simplePage(response, "{\"result\":\"fail\"}");
return true;
}
if(!file.isFile()){
System.out.println("파일이 아님!");
simplePage(response, "{\"result\":\"fail\"}");
return true;
}
// 파일 삭제 하기.
if(file.delete()){
re = "success";
}
}catch(Exception e){
e.printStackTrace();
re = "fail";
}
simplePage(response, "{\"result\":\""+re+"\"}");
return true;
}
- 수정 게시글 저장하기
- 폴더 명을 변경하여 변경한 폴더에 사용자가 보낸 파일을 저장한다.
public class MainPage extends HttpServlet{
... 생략 ...
private boolean uriSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 글 수정 저장하기
if(request.getMethod().equals("POST") && uri.equals(noticeBoardModifySave)) {
try {
... 생략 ...
// 전 저장위치
var noticeSID = notice.getSid();
var beforeFolderName = notice.getGenTime()+"_"+noticeSID;
var beforeUploadPath = getFilePath(request, beforeFolderName);
// 글 삭제
if(!noticeDAO.deleteNotice(sid)){ // 삭제 실패
return false;
}
// 새로운 글 저장
var genTime = System.currentTimeMillis();
var newSid = noticeDAO.saveNotice(user, title, mainText, genTime);
if(newSid==0){ // 실패
result = "<script>alert('fail');location.href='"+noticeBoard+"'</script>";
simplePage(response, result);
return true;
}
var noticeRe = "no new file";
try {
var folder = new File(beforeUploadPath); // 폴더
var newFolderName = genTime+"_"+newSid;
var afterUploadPath = getFilePath(request, newFolderName);
var newFolder = new File(afterUploadPath);
if(folder.exists()){
// 폴더 이름만 변경.
folder.renameTo(newFolder);
}
var parts = request.getParts();
for(var part:parts){
if(!part.getHeader("Content-Disposition").contains("filename=")){
continue;
}
if(part.getSubmittedFileName().equals("")){
continue;
}
if(!newFolder.exists()){
// 폴더 생성
newFolder.mkdir();
System.out.println(newSid+"게시글 폴더 생성 함");
}
var count = newFolder.listFiles().length;
System.out.println("파일 갯수 : "+count);
if(count > MAX_FILE_NUM){
System.out.println("최대 파일 갯수 초과");
noticeRe = "out of file number ( maxcount 3 )";
break;
}
System.out.println("퐅더 존재? => "+newFolder.isDirectory());
System.out.println("타입 : "+part.getHeader("Content-Disposition"));
System.out.println("크기 : "+part.getSize());
System.out.println("이름 : "+part.getSubmittedFileName());
part.write(afterUploadPath+File.separator+part.getSubmittedFileName());
part.delete(); // 임시 파일 삭제
System.out.println("파일 저장 성공");
noticeRe = "file upload success";
System.out.println("경로 : "+afterUploadPath);
}
} catch (Exception e) {
e.printStackTrace();
noticeRe = "file upload fail";
}
// 성공
result = "<script>alert('success : "+noticeRe+"');location.href='"+noticeBoard+"'</script>";
simplePage(response, result);
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}
}
파일 다운로드
- MIME를 이용하여 대용량 파일을 텍스트 문자 인코딩하여 전송한다.
게시판 첨부파일 다운로드
JSP
설명
- 게시글 페이지 요청이 들어오면 첨부파일 링크도 추가한 게시글 페이지를 보여준다.
- 게시글에 업로드 된 파일의 다운로드 링크를 만들어준다.
- 다운로드 링크를 누르면 서버에 저장된 파일을 다운받을 수 있다.
- 첨부파일이 없을 경우 링크는 나타나지 않는다.
과정
- 사용자가 첨부파일 다운로드 링크를 클릭한다.
- 사용자는 서버로 파일 다운로드 get 요청을 보낸다.
- 서버는 사용자에게 파일을 버퍼로 쪼개어 응답을 보내준다.
- 다운로드 완료
코드 ( notice.jsp )
- 파일 이름과 파일 위치 배열
- servlet으로 부터 배열을 받아온다.
<%
var fileNameList = (ArrayList<Pair<String,String>>)request.getAttribute("fileNameList");
%>
- 게시글 파일 다운로드 링크
<% if(fileNameList.size() > 0) { %>
<tr>
<td style="text-align: center;">
<span>첨부파일</span>
</td>
<td>
<% for(var file : fileNameList ) { %>
<a href="${noticeBoardFileDownload}?downlink=<%=file.getD2() %>"><%=file.getD1() %></a><br>
<% } %>
</td>
</tr>
<% } %>
Servlet
설명
- 게시글 페이지 요청이 들어오면 첨부파일 링크도 추가한 게시글 페이지를 보여준다.
- 다운로드 요청이 들어오면 서버에서 파일을 버퍼 단위로 쪼개어 사용자에게 전송한다.
- 다운로드 파일이 없으면 404 페이지를 보여준다.
과정
- 사용자가 다운로드 링크를 눌러 서버에 파일 다운로드 요청을 보낸다.
- 서버는 가장먼저 서버에 파일이 존재하는지 확인한다.
- 서버는 어느 브라우저에서 온 요청인지 확인한다.
- 위에서 알아본 정보를 가지고 응답 헤더를 작성한다.
- 서버는 파일을 버퍼 단위로 나누어 사용자에게 전달한다.
코드 (MainPage.java)
- 게시글 파일 다운로드
- 사용자 브라우저가 모질라일 때만 다운로드가 가능하다.
- 헤더에 파일 사이즈를 입력하면 브라우저가 다운완료 시간을 예측한다.
public class MainPage extends HttpServlet{
... 생략 ...
private boolean uriSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 게시글 파일 다운로드
if(uri.equals(noticeBoardFileDownload)){
var downlink = request.getParameter("downlink");
var filePath = new String(getFilePath(request, downlink).getBytes("UTF-8")); // utf-8로 바꿔준다.
var file = new File(filePath);
if(!file.exists()){
return false;
}
if(!file.isFile()){
return false;
}
var filesize = file.length();
var sMimeType = getServletContext().getMimeType(filePath); // 확장자에 따라 달라진다.
if(sMimeType == null || sMimeType.length() == 0){
sMimeType = "application/octet-stream";
}
BufferedInputStream fin = null;
BufferedOutputStream outs = null;
try {
var fileName= downlink.split("/")[1];
byte[] b = new byte[8192];
response.setContentType(sMimeType+"; charset=utf-8");
var userAgent = request.getHeader("User-Agent");
System.out.println(userAgent);
if(userAgent != null && userAgent.contains("MSIE 5.5")){ // MSIE 5.5 이하
return false;
}else if(userAgent != null && userAgent.contains("MSIE")){ // MS IE
return false;
}else{ // 모질라
response.setHeader("Content-Disposition", "attachment; filename="+ new String(fileName.getBytes("utf-8"), "latin1") + ";");
}
// 파일 사이즈 정확히 알아야함
if(filesize > 0){
response.setHeader("Content-Length", ""+filesize);
}
fin = new BufferedInputStream(new FileInputStream(file));
outs = new BufferedOutputStream(response.getOutputStream());
int read = 0;
while((read= fin.read(b)) != -1){
outs.write(b, 0, read);
}
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try{
fin.close();
}catch(Exception e){}
try{
outs.close();
}catch(Exception e){}
}
return true;
}
- 글 자세히 보기
- href 링크를 만들어 jsp 에 전달해 준다.
public class MainPage extends HttpServlet{
... 생략 ...
private boolean uriSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 글 자세히 보기
if(request.getMethod().equals("GET") && uri.equals(noticeBoardDetail)){
try {
... 생략 ...
// *** 파일들 읽어 오기 ***
// href 링크 만들기
var folderName = notice.getGenTime()+"_"+noticeSID;
var uploadPath = getFilePath(request, folderName);
// 파일 다운로드 get 파라미터 이름
var fileNameList = new ArrayList<Pair<String,String>>();
try{
var folder = new File(uploadPath); // 폴더
if(folder.exists()){
var files = folder.listFiles(); // 파일들
for(var file : files) {
var fileName = file.getName();
var hrefPrameter = folderName+"%2F"+fileName;
fileNameList.add(new Pair<String,String>(fileName,hrefPrameter));
System.out.println(fileNameList);
}
}
}catch(Exception e){
e.printStackTrace();
}
request.setAttribute("fileNameList", fileNameList);
request.setAttribute("noticeBoardFileDownload", noticeBoardFileDownload);
// *** 파일들 읽어 오기 end ***
request.setAttribute("notice", notice);
if(user.getSid() == notice.getUserSID()){
request.setAttribute("modifyButton", true);
request.setAttribute("modifyHref", noticeBoardModify);
request.setAttribute("deleteHref", noticeBoardDelete);
}
gotoForwardPage(request, response, "/WEB-INF/main_page/notice_board/notice_detail.jsp");
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}
}
공통 코드
- MainPage.java 의 getFilePath 함수
private String getFilePath(ServletRequest request,String folderName){
var path = request.getServletContext().getRealPath("/WEB-INF/upload_folder/"+folderName);
return path;
}
게시글 조회수
DB
- 조회수 컬럼(views 컬럼) 게시판 테이블에 추가.
alter table notice_board add views number(20) default 0 not null;
# 조회수 값 1 증가 하기.
update notice_board set views = 1 where sid=101;
JSP
설명
- 사용자가 게시글 방문하면 게시글 조회수는 1 증가한다.
- 사용자는 게시글의 조회수를 확인할 수 있다.
과정
- 사용자는 서버로 게시글 페이지를 요청한다.
- 서버에서 게시글 조회수를 1 증가 시킨다.
- 게시글 페이지를 완성하여 사용자에게 전달한다.
코드 ( notice.jsp )
- 조회수 가져오기
- notice는 servlet 에서 가져온 것이다.
<%=notice.getViews() %>
Servlet
설명
- 사용자가 게시글에 방문하면 table의 조회수 값을 1증가 시킨다.
과정
- 사용자는 서버로 게시글 페이지를 요청한다.
- 서버는 DB 서버의 게시판 테이블에 조회수 값을 1증가 시켜 저장한다.
- 서버는 게시글 페이지를 완성하여 사용자에게 전달한다.
코드 ( MainPage.java )
- 조회수 1 증가 하기
public class MainPage extends HttpServlet{
... 생략 ...
private boolean uriSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
... 생략 ...
// 글 자세히 보기
if(request.getMethod().equals("GET") && uri.equals(noticeBoardDetail)){
try {
// 게시글 조회수 1 증가 시키기.
var noticeSID = notice.getSid();
noticeDAO.incressViews(noticeSID, notice.getViews());
notice = noticeDAO.getNotice(noticeSID);
if(notice == null){
return false;
}
// 게시글 조회수 증가 end
코드 ( Notice.java)
- 조회수 부분을 추가하였다.
public class Notice {
private long views;
public Notice(
... 생략 ...,
long views
){
... 생략 ...
}
public long getViews(){
return views;
}
... 생략 ...
코드 ( NoticeBoardDAO.java )
- DB의 게시판 테이블에 저장된 조회수 값을 1 증가시킨다.
public class NoticeBoardDAO {
... 생략 ...
// 조회수 1 카운트 증가시키기
public long incressViews(long sid, long currentViews){
var query = new StringBuilder();
query.append("update notice_board set views = ");
query.append(currentViews+1);
query.append(" where sid=");
query.append(sid);
try{
Connection();
var pstmt = conn.prepareStatement(query.toString());
var re = pstmt.executeUpdate();
System.out.println(sid+" 글 조회수 업데이트 : "+re);
}catch(Exception e){
e.printStackTrace();
}finally{
Close();
}
return sid;
}
발생한 오류
- Unable to process parts as no multi-part configuration has been provided
var parts = request.getParts();
Unable to process parts as no multi-part configuration has been provided
web.xml 에 <multipart-config> 설정을 해 줘야 한다.
- 413 Request Entity Too Large
nginx.conf
http {
client_max_body_size 6M;
...
}
후기
- 직접 업로드와 다운로드 기능을 만들면서 어떤 방식으로 동작하는지 알 수 있는 계기가 되었다. 특히 다운로드 기능을 구현할 때 인코딩 에러가 발생하여 당황스러웠지만 응답 헤더를 고치니 해결되어 다행이었다.
- 조회수를 구현은 생각보다 간단하였다.
참고 사이트
- Java 자바로 폴더(디렉토리) 삭제하기(하위파일, 폴더 포함) (tistory.com)
- Java 자바로 폴더(디렉토리) 생성하기 (tistory.com)
- Java 파일, 디렉토리 존재 여부 확인하기 - 어제 오늘 내일 (tistory.com)
- 코끼리를 냉장고에 넣는 방법 :: 서블릿/JSP Servlet 3.0에서 Part API를 통한 파일업로드 구현하기 (tistory.com)
- How to add file uploads function to a webpage in HTML ? - GeeksforGeeks
- HTTP GET 방식으로 서버에 요청하기 (작성중) (tistory.com)
- 자바(JAVA) - 폴더 파일명 변경 (tistory.com)
- WEBDIR :: 폼 필드(input type=”file”) 디자인 #4 (tistory.com)
- JavaScript - 파일 용량 제한 - CODE:H (hhyemi.github.io)
- fetch()로 원격 API 호출하기 (tistory.com)
- Javascript/Jquery TABLE 행 추가, 삭제, 체크된 table row 삭제 방법 (tistory.com)
- JavaScript - JSON 데이터 변환, 추출, 원하는 데이터 찾기 (tistory.com)
- Fetch 사용하기 - Web API MDN (mozilla.org)
- java:servlet:download 권남 (kwonnam.pe.kr)
- MIME이란 무엇인가? (tistory.com)
- nginx 413 Request Entity Too Large 오류 (leocat.kr)
댓글남기기