YJ의 새벽

JDBC 활용 연습용8. ( 로그인후 마이페이지 창 (사진업로드) ) 본문

SelfStudy/JDBC

JDBC 활용 연습용8. ( 로그인후 마이페이지 창 (사진업로드) )

YJDawn 2023. 3. 30. 18:27

 

 

enctype : form 태그가 데이터를 서버로 제출할 때 
                              데이터의 인코딩 형식을 지정하는 속성
            1) application/x-www-form-urlencoded
                        - 모든 문자를 서버로 제출하기 전에 인코딩 (모든 데이터가 문자)
                          (form태그 기본값)

             2) multipart/form-data : 제출할 때 인코딩을 하지 않음
                        -> 모든 데이터가 원본 형태를 유지(파일이 파일상태로 서버로 제출)
                        (주의) multipart/form-data 로 설정 시 method는 무조건 POST

 

 

 

--- 파일명 바꾸기위해.  MyRenamePolocy.class 생성 . 

// 파일명 변경 정책
public class MyRenamePolicy implements FileRenamePolicy{
	
	@Override
	public File rename(File originalFile) {

		long currentTime = System.currentTimeMillis();
		// 1970년 1월1일 오전9시 부터 현재시간까지 경과한 ms 를 반환
		
		SimpleDateFormat ft = new SimpleDateFormat("yyyyMMddHHmmss");
		
		int ranNum =(int)(Math.random() * 100000);  //  0 <=  ranNum  <= 99999 
		
		String str = "_" +String.format("%05d", ranNum);
		               // user.png
		               // 2023033012051_00100.png 
		
		// String.format  :  문자열을 지정된 패턴의 형식으로 변경하는 메서드
		// %05d : 오른쪽 정렬된 십진정수(d) 5자리형태로 변경, 빈자리는 0으로 채움
	
		// 파일명을 변경해도 확장자를 유지하기 위하여 별도로 확장자 명 가져오기
		String name = originalFile.getName();
		String ext = null;
		
		int dot = name.lastIndexOf(".");   // "." 없으면 -1 반환 . 
		
		if(dot != -1) {
			//dot 포함해서 확장자 추출
			ext = name.substring(dot);
		}else {
			ext = "";
		}
		
		String fileName = ft.format(new Date(currentTime)) + str + ext;
		                      // 2023033012312 + _01234 + .png
		
		File newFile = new File(originalFile.getParent(), fileName );
		                     // 경로명 문자열을 반환 or 부모디렉토리의 이름을 지정하지않으면 null.
		return newFile;
	}
}

 

 

 

-- myPage-profile.jsp 

                <form action="profile" method="POST" name="myPage-form" 
                      enctype="multipart/form-data" onsubmit="return profileValidate()">

                    <div class="profile-image-area">
                        <c:if test="${empty loginMember.profileImage}">
                            <img src="${contextPath}/resources/images/user.png" id="profile-image">
                        </c:if>

                        <c:if test="${!empty loginMember.profileImage}">
                            <img src="${contextPath}/${loginMember.profileImage}" id="profile-image">
                        </c:if>

                        <!-- 프로필 이미지 삭제 버튼 -->
                        <span id="delete-image">x</span>
                    </div>

                    <div class="profile-btn-area">
                        <label for="input-image">이미지 선택</label>
                        <input type="file" name="profileImage" id="input-image" accept="image/*">
                        <!-- accept="image/*" : 이미지 파일 확장자만 선택 허용 -->
                        <!-- accept="video/*" : 동영상 파일 확장자만 선택 허용 -->
                        <!-- accept=".pdf" : pdf파일만 선택 허용 -->

                        <button type="submit">변경하기</button>
                    </div>


                    <div class="myPage-row">
                        <label>이메일</label>
                        <span>${loginMember.memberEmail}</span>
                    </div>

                    <div class="myPage-row">
                        <label>가입일</label>
                        <span>${loginMember.enrollDate}</span>
                    </div>

                    <!-- 삭제버튼(x)이 눌러짐을 기록하는 숨겨진 input 태그 -->
                    <!-- 0 : 안눌러짐   /   1: 눌러짐 -->
                    <input type="hidden" name="delete" id="delete" value="0">
                </form>

 

 

 

 

 

 

 

 

 

 

 

-- MyPageProfileServlet.class 추가    ( /member/myPage/profile )

@WebServlet("/member/myPage/profile")
public class MyPageProfileServlet extends HttpServlet{
	
	@Override    //  메인프로필 --> 프로필이미지클릭 --> jsp 이동
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
		// a 태그로 받아서 --> jsp 로 넘김
		String path = "/WEB-INF/views/member/myPage-profile.jsp";
		req.getRequestDispatcher(path).forward(req, resp);
	}

	
	@Override    //  프로필 이미지 업로드 --> 변경 요청  ( jsp의 form 에서 받아옴 )
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		try {
		
		System.out.println(req.getParameter("profileImage"));
		
		// 파라미터가 얻어와지지 않는 이유 !
		// 1. enctype= "multipart/form-data"  -> 인코딩이 안되어있어서 파라미터 인지되지 않음.
		// 2. input type="file"    ->  파일형태 데이터
		
		// -> multipart/form-data  형식의 요청을 처리할수있는 전용 Request 객체가 필요 !! 

		// --> MultipartRequest   ( cos.jar 라이브러리 이용 ) 파일 업로드용 라이브러리
		
		// ****************** MultipartRequest 사용위한 준비단계 **********************
		
		// 1. 업로드되는 파일 크기의 전체 합을 지정  ( byte 지정 )
		int maxSize = 1024 * 1024 * 20;
		           //  1KB * 1MB * 20MB
	               // 1 KB = 1024byte
		           // 1 MB = 1024KB
		// 2. 업로드되는 파일이 어디에 저장될지 경로 지정
		//   -> 서버 컴퓨터 내부 폴더
		
		// 2-1 .  server option 확인 .
		//  server  ->  설정선택  -> Overview  -> server option -> 
		//      Serve Modules Without Publishing 체크 . 
		//   (업로드 되는 파일이 webapp폴더 내부에 저장될수 있음)
		// 2-2 . memberProfile 폴더까지의 절대경로 얻어오기
		
		HttpSession session = req.getSession();
		// 최상위 경로의 컴퓨터상 실제 절대 경로를 얻어옴 .
		String root = session.getServletContext().getRealPath("/");
		
		//실제 파일이 저장되는 폴더의 경로
		String folderPath = "resources/images/memberProfile/";
		
		// memberProfile 폴더 까지의 절대경로
		String filePath = root + folderPath;
		
		// 3. 저장되는 파일의 파일명 중복을 방지하기위한 파일명 변경규칙
		//    -> cos.jar  에서 제공하는  FileRenamePolicy  인터페이스 상속 클래스 생성
		//    --> MyRenamePolicy 클래스 생성
		
		// 4. 파일 이외의 파라미터들의 문자 인코딩 지정
		String encoding = "UTF-8";
		
		// 5. MultipartRequest 생성
		// MultipartRequest 객체가 생성됨과 동시에 지정된 경로에
		// 지정된 파일명 변경정책에 맞게 이름이바뀐파일이 저장 (서버 업로드) 된다.
		MultipartRequest mpReq 
		              = new MultipartRequest(req,filePath,maxSize,encoding,new MyRenamePolicy());
		
		// 프로필 이미지 변경 Service 호출 시 필요한 값
		// 1) 로그인한 회원 (Session의 loginMember) 의 회원번호
		Member loginMember =(Member)session.getAttribute("loginMember");
		int memberNo = loginMember.getMemberNo();
		
		// 2) 업로드된 프로필 이미지의 웹 접근경로(folderPath + 변경된 파일명)
		
		// getOriginalFileName("input type='file'의 name속성값")
		//  -> 원본 파일명  ex) user.png
		
		// getFilesystemName("input type='file'의 name속성값")
		//  -> 변경된 파일명  ex) 2023033012314_03124.png 
		
		String profileImage = folderPath + mpReq.getFilesystemName("profileImage");
		// DB에 삽입될 프로필 이미지 경로
		// X 버튼이 클릭된 상태면 null을 가지게 한다.
		
		// **** 프로필이미지 삭제 **********
		// 1) "delete" input type="hidden" 태그의 값(파라미터) 얻어오기
		// (주의!!) Multipart/form-data 형식의 요청을 처리중이기때문에
		//   req를 이용해서 파라미터를 얻어올수 없다 !!! 
		//    --> mpReq 를 이용하면 가능 !! 
		
		int delete =  Integer.parseInt(mpReq.getParameter("delete"));
		
		// 2) delete 의 값이 1이면 (눌러진경우) profileImage값을 null로 변경
		if ( delete ==1 ) {
			profileImage = null;
		}
		
		// 프로필 이미지 변경 Service 호출 후 결과 반환 받기
		MemberService service = new MemberService();
		
		int result = service.updateProfileImage(memberNo, profileImage);
		
		if(result > 0) {  // 성공
			session.setAttribute("message", "프로필 이미지가 변경되었습니다.");
			
			// DB의 프로필 이미지 정보는 변경되었으나
			// Session 에 저장된 로그인 정보중 프로필 이미지는 변경되지않음.
			//   --> 동기화 작업 진행
			loginMember.setProfileImage(profileImage);  // 세션의 값이 변경됨.
			
		}else {  // 실패
			session.setAttribute("message", "프로필 이미지 변경실패 ㅜㅜ");
		}
		
		// 성공/ 실패 관계없이 프로필화면 재요청 (redirect)
		resp.sendRedirect("profile");  // 상대경로 (GET)
		
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

 

 

 

 

 

 

--- MemberService.class   프로필이미지변경 서비스 추가

	/**  프로필 이미지 변경 서비스
	 * @param memberNo
	 * @param profileImage
	 * @return
	 */
	public int updateProfileImage(int memberNo, String profileImage) throws Exception{
		
		Connection conn = getConnection();
		
		int result = dao.updateProfileImage(conn, memberNo, profileImage);
		
		if (result > 0) commit(conn);
		else            rollback(conn);
		
		close(conn);
		
		return result;
	}
}

 

--- MemberDAO.class    프로필이미지 변경 DAO  추가

	/**  프로필 이미지 변경 DAO
	 * @param conn
	 * @param memberNo
	 * @param profileImage
	 * @return
	 * @throws Exception
	 */
	public int updateProfileImage(Connection conn, int memberNo, String profileImage) throws Exception{
		int result=0;
		
		try {
			String sql = prop.getProperty("updateProfileImage");
			
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, profileImage);
			pstmt.setInt(2, memberNo);
			
			result = pstmt.executeUpdate();
			
		}finally {
			close(pstmt);
		}
		return result;
	}
}

 

 

--- member-sql.xml    프로필이미지변경 sql 문 추가

	<entry key="updateProfileImage">
	UPDATE MEMBER SET
	PROFILE_IMG = ?
	WHERE MEMBER_NO = ?
	</entry>

 

 

-- myPage-profile  에 쓸  myPage.js  추가    ( 이미지 미리보기 )

 

// 회원 프로필 이미지 변경 ( 미리보기 )
const inputImage = document.getElementById("input-image");

if ( inputImage != null ){  // inputImage 요소가 화면에 존재할때.
    
    // input type="file"  요소는 파일이 선택될때 change 이벤트 발생
    inputImage.addEventListener("change",function(){

        // 파일 목록에서 첫번째 파일 객체를 선택
        // files : input type="file" 만 사용가능한 속성으로
        //         선택된 파일목록(배열) 을 반환

        if ( this.files[0] != undefined){  // 파일이 선택되었을때

            const reader = new FileReader();
            // 자바스크립트의 FileReader 
            //  - 웹 애플리케이션이 비동기적으로 데이터를 읽기위하여 사용하는객체

            reader.readAsDataURL(this.files[0]);
            // FileReader.readAsDataURL ( 파일 )
            // - 지정된 파일의 내용을 읽기 시작함.
            // - 읽어오는게 완료되면 result 속성에 data에
            //   읽어온 파일의 위치를 나타내는 URL이 포함된다.
            //  -> 해당 URL을 이용해서 브라우저에서 이미지를 볼수 있다.

            reader.onload = function(e){
                // e : 이벤트 발생 객체
                // e.target : 이벤트가 발생한 요소 --> FileReader
                // e.target.result : FileReader가 읽어온 파일의 URL
                
                // 프로필 이미지의 src 속성의 값을 FileReader 가 읽어온 파일의 URL로변경
                const profileImage = document.getElementById("profile-image")

                profileImage.setAttribute("src", e.target.result);
                // -> setAttribute() 호출시 중복되는 속성이 존재하면 덮어쓰기.

                document.getElementById("delete").value = 0;
                // 새로운 이미지가 선택되었기 때문에 1 -> 0 (안눌러짐) 으로 변경
            }
        }
    });
}

// 프로필 이미지 옆 X 버튼 클릭 시
document.getElementById("delete-image").addEventListener("click",function(){
    // 0 : 안눌러짐
    // 1 : 눌러짐

    const del = document.getElementById("delete");

    if( del.value == 0 ){  // 눌러지지 않은경우
        // 1) 프로필 이미지를 기본 이미지로 변경
        document.getElementById("profile-image").setAttribute("src",contextPath+"/resources/images/user.png");

        // 2) input type="file" 에 저장된값(value) 에 "" 대입
        document.getElementById("input-image").value = "";

        del.value = 1;
    }
});


// 이미지 선택 확인 .
function profileValidate(){
    const inputImage = document.getElementById("input-image");

    const del = document.getElementById("delete"); // hidden 타입

    if( inputImage.value == "" && del.value == 0){
        // 파일선택 X      &&    X 를 누르지도 않음
        // --> 아무것도 안하고 변경버튼만 클릭한 경우
        alert("이미지를 선택한 후 변경버튼을 클릭해주세요.");
        return false;
    }
    return true;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Comments