이전 게시글은 여기 >>
Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(18) - 키워드를 이용한 검색 만들기
파일에 사진을 적용시키려면 먼저 파일을 업로드 해야한다. 이제 섬머노트 같은거 이용 안하고 직접 파일을 올려서 적용시킨다.
파일을 올리기 위해서는 input type=file을 이용해서 파일을 업로드 한다.
파일을 업로드 하기 위해서 서블릿, jsp에 제일 많이 이용하는 라이브러리는 cos 라이브러리다.
다음 파일을 다운로드 받으면 내부에 jar 파일이 있다. 그것을 라이브러리에 빌드패스 하면 된다.
먼저 화면에 nav bar에 프로필 사진을 띄워볼 것이다.
(아 말로 하면 애매하니까 최종 결과사진부터)
먼저 nav에 저 버튼(?)을 넣어야 하지.
저 버튼은 로그인이 되야 하니까, 세션에 인증값이 있으면 뜨도록 한다.
또, 저 링크 그룹과 사진이 서로 떨어지도록 폼 그룹에 justify-content-between을 설정한다.
- nav.jsp 의 body 밑 부분
<ul class="navbar-nav">
<c:if test="${not empty sessionScope.principal}">
<li class="nav-item" >
<a href="/blog/user?cmd=profileUpload">
<img style="border-radius:20px" onerror="this.src='/blog/image/userProfile.png'" src="${sessionScope.principal.userProfile }" width="40px" height="40px">
</a>
</li>
</c:if>
</ul>
(사진이 가로세로 40px 이니까 radius=20px 을 걸어두면 완전히 동글동글한 모양이 나온다.
이제 저 사진을 누르면 사진을 등록하는 페이지가 나오도록 할 것이다. 그러니까 당연하게 router에 할 일을 지시하는 코드를 걸어줘야 겠다.
-UserController.java 의 router부분
public Action router(String cmd) {
if(cmd.equals("join")) {
return new UsersJoinAction();
}else if(cmd.equals("joinProc")) {
//회원가입 진행 후 -> index.jsp 이동
return new UsersJoinProcAction();
}else if(cmd.equals("update")) {
return new UsersUpdateAction();
//회원 수정 페이지 이동(세션에 user object를 가지고 있을 예정)
}else if(cmd.equals("updateProc")) {
//회원 수정 진행 -> index 이동
return new UsersUpdateProcAction();
}else if(cmd.equals("delete")) {
//회원 삭제 진행 후 -> 로그아웃 -> index.jsp
}else if(cmd.equals("login")) {
//회원 로그인 페이지로 이동
return new UsersLoginAction();
}else if(cmd.equals("loginProc")) {
return new UsersLoginProcAction();
//로그인 수행 -> 세션 등록 -> index.jsp
}else if(cmd.equals("logout")) {
return new UsersLogoutAction();
//로그아웃 수행 -> index.jsp
}else if(cmd.equals("usernameCheck")) {
return new UsersUsernameCheckAction();
//아이디와 일치하는 이름이 있는지, 수행 -> index.jsp
}else if(cmd.equals("profileUpload")) {
return new UsersprofileUploadAction();
//아이디와 일치하는 이름이 있는지, 수행 -> index.jsp
}else if(cmd.equals("profileUploadProc")) {
return new UsersprofileUploadProcAction();
//아이디와 일치하는 이름이 있는지, 수행 -> index.jsp
}
return null;
}
먼저 프로필을 바꿀 수 있는 화면으로 보내주는 액션을 만들자.
-UsersprofileUploadAction.java
package com.cos.blog.action.user;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.cos.blog.action.Action;
import com.cos.blog.util.Script;
public class UsersprofileUploadAction implements Action {
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
if (session.getAttribute("principal") == null) {
Script.outText("잘못된 접근입니다.", response);
}
RequestDispatcher dis=request.getRequestDispatcher("user/profileUpload.jsp");
dis.forward(request, response);
}
}
평범하게(??) 화면으로 보내는 액션이다. 프로필 변경화면으로 보낸다.
그럼 보낸 화면을 구현해야한다.
-profileUpload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="../include/nav.jsp" %>
<div class="container">
<div class = "d-flex justify-content-center">
<form action="/blog/user?cmd=profileUploadProc" method="post" enctype="multipart/form-data">
<div class="form-group" >
<img id="img__wrap" onerror="this.src='/blog/image/userProfile.png'" src="${sessionScope.principal.userProfile }" width="350px" height="300px" />
</div>
<div class="form-group bg-light">
<input type="file" name="userProfile" id="img__preview"/> <!-- 변수명은 보통 __로 구분 -->
</div>
<div class="form-group">
<button class="btn btn-primary w-100">사진 전송</button>
</div>
<input type="hidden" name="id" value="${sessionScope.principal.id }"/>
</form>
</div>
</div>
<script src="/blog/js/imgPreview.js"></script>
<%@ include file="../include/footer.jsp" %>
이 화면을 정적인 화면으로 보면 다음과 같다.
이 가운데 오는 화면에는 비밀(?)이 있다.
단순히 이 화면을 가운데 오게 하기 위해 d-flex 속성을 걸면 input과 img 태그가 따로따로 inline-block 속성이 적용되어 다음과 같이 한 줄로 와버리게 된다.
그렇기 때문에 폼 태그를 다시 묶어줄 div 태그를 다시 만들어서 그 태그에 d-flex속성을 적용시켜 inline-block 속성을 적용시켜 마치 하나의 요소처럼 만든다.
이 화면에는 두 가지의 요소가 있다.
1. img 화면에 미리 보기 출력
2. 사진의 경로를 지정해서 데이터베이스에 넣어주기.
첫번째부터 보자.
이건 자바스크립트에서 script를 이용해, 파일에서 선택되어 파일 이름이 변경될 때마다 스크립트의 이벤트가 동작하도록 한다.
따라서, 파일에 id 속성을 걸어서 이벤트를 걸 태그를 지정해서 다음 이벤트를 진행한다.
-imgPreview.js
$("#img__preview").on("change", function(e){
var f=e.target.files[0];
if(!f.type.match("image*")){
alert("이미지만 첨부할 수 있습니다..");
$("#img__preview").val('');
return;
}
// f.size = 1024*1024*2
if(f.size>1024*1024*2){
alert("2mb까지의 사진만 업데이트 할 수 있습니다.");
$("#img__preview").val('');
return;
}
var reader=new FileReader();
reader.onload=function(e){
$("#img__wrap").attr("src",e.target.result);
}
reader.readAsDataURL(f); //비동기적 진행(파일 읽기)
});
처음 두 가지의 if문을 통해 예외상황 두 가지를 처리한다.
1) 이미지 이외의 파일을 등록할때
2) 2mb 이상의 큰 파일을 등록할때
사진의 타입의 파일타입이 이미지가 아니라면 사진의 value를 비우게 된다.
파일은 readonly 타입이기 때문에 이렇게 강제적으로 태그를 지정해서 '' 로 비워줘야 한다.
또, 1024*1024가 1mb이기 때문에 2*1024*1024가 2mb이다. 따라서 파일의 사이즈(f.size)가 값 이상일 때도 값을 비워버린다.
이 두 조건을 통과한 파일은 reader로 받아온 파일을 읽고, 파일이 이벤트로 인해 로드 된다.
이 filereader 속성은 비동기적 통신을 하기 때문에, 이것과 관련된 이벤트가 이 파일이 로드될 때 동작할 수 있도록 해야하는데, 이걸 도와주는 속성이 readAsDataURL이다.
readAsDataURL 속성에 의해 파일이 로딩되어야만 트리거가 작동되도록 조치된다.
이제 사진 전송을 눌렀을 때 데이터베이스에 값을 넣어보자.
- UserprofileUploadProcAction.java
package com.cos.blog.action.user;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.cos.blog.action.Action;
import com.cos.blog.model.Users;
import com.cos.blog.repository.UsersRepository;
import com.cos.blog.util.Script;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
public class UsersprofileUploadProcAction implements Action {
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
int id;
String fileName=null;
String realPath=request.getServletContext().getRealPath("image");
String contextPath=request.getServletContext().getContextPath();
String userProfile=null;
System.out.println("realPath : "+realPath);
System.out.println("contextPath : "+contextPath);
try {
MultipartRequest multi=new MultipartRequest(
request,
realPath,
1024*1024*2,
"UTF-8",
new DefaultFileRenamePolicy()
);
fileName=multi.getFilesystemName("userProfile");
System.out.println("fileName:"+fileName);
id=Integer.parseInt(multi.getParameter("id"));
System.out.println(id);
userProfile=contextPath+"/image/"+fileName; // blog/image/파일 이름
UsersRepository usersRepository=UsersRepository.getInstance();
int result=usersRepository.update(id,userProfile);
if(result==1) {
HttpSession session=request.getSession();
Users principal=usersRepository.findById(id);
session.setAttribute("principal", principal);
Script.href("사진 변경 완료","/blog/index.jsp", response);
}else {
Script.back("사진 변경 실패", response);
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
Script.outText("오류 : "+e.getMessage(), response);
}
}
}
contextPath는 컨텍스트 루트의 경로를, realPath는 사진이 저장되는 실제 경로를 의미한다.
이후 cos의 multipartRequest를 이용해서 텍스트 이외에 다양한 타입의 데이터를 받을 수 있도록 선언하는데, 이 때 요청값, 사진의 경로와 사진의 크기, 사진의 인코딩 기법, 사진의 이름이 중복될 때 사진의 이름을 정하는 기법(cos 라이브러리 함수)을 정하는데 이 기법은 동일한 이름이 들어올 경우 aaa, aaa1, aaa2, .. , 이런 방식으로 값을 저장해준다.
이후에 multi 함수를 통해 요청받은 값을 가져와서 이 값을 컨텍스트 루트와 합친 후 userRepository의 update 함수를 이용해서(오버로딩한 값임) 저장한다.
-userRepository.java 중 update 오버로딩 부분
public int update(int id,String UserProfile) {
final String SQL="update users set UserProfile = ? where id=?";
try {
conn=DBConn.getConnection();
pstmt = conn.prepareStatement(SQL);
//물음표 완성하기
pstmt.setString(1, UserProfile);
pstmt.setInt(2, id);
System.out.println(pstmt.executeUpdate());
return pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
System.out.println(TAG+"update : "+e.getMessage());
}finally {
DBConn.close(conn, pstmt);
}
return -1;
}
값이 정상적으로 저장되면, 홈페이지로 되돌려준다.
이 코드가 정상적으로 돌아가면 앞서 보았던 페이지처럼 오른쪽 위에 사진이 변하게 된다.