재밌고 어려운 IT를 이해해보자~!
Image Upload Update [TEAM PROJECT] 본문
이미지파일 선택시 preview를 생성해서 유저에게 보여주고 이미지의 사이즈를 프로필 사이즈에 맞게 조정
main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="model.member.*"%>
<%@ taglib tagdir="/WEB-INF/tags" prefix="stone"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<!-- jQuery 추가 -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<meta charset="UTF-8">
<title>마이페이지</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<link rel="stylesheet" href="${pageContext.request.contextPath}/assets/css/main.css" />
<noscript>
<link rel="stylesheet" href="assets/css/noscript.css" />
</noscript>
<style>
input::-webkit-input-placeholder {
font-family: "Source Sans Pro", Helvetica, sans-serif;
}
a {
font-family: "Source Sans Pro", Helvetica, sans-serif;
}
.field {
margin-bottom: 50px;
}
.photo {
width: 350px;
height: 350px;
border: 3px solid black;
overflow: hidden;
}
.field input[type="text"] {
display: inline-block;
width: 60%;
font-weight: bold;
}
.btn-upload {
width: 150px;
height: 50px;
margin-top: 10px;
background: #fff;
border: 1px solid rgb(77, 77, 77);
border-radius: 10px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.btn-upload:hover {
background: rgb(77, 77, 77);
color: #fff;
}
#fileInput {
display: none;
}
</style>
</head>
<body class="is-preload">
<stone:printNav member='${member}'/>
<div id="footer">
<section>
<div class="fields">
<h1 style="font-size: 150px; text-align: center;">MyPage</h1>
<form id="firstForm" action="profileUpload.do" method="post" enctype="multipart/form-data">
<!-- 프사 -->
<div class="field">
<div class="photo">
<img id="preview" alt="프로필 이미지" src="mimg/${memberDTO.profile}" onload="resizePreviewImage(this, 350, 350)">
</div>
<label for="fileInput" class="btn-upload">이미지 선택 <input type="file" name="file" id="fileInput" form="firstForm"></label>
</div>
</form>
<!-- 아이디 -->
<div class="field">
<label for="myPageID"></label> <input type="text" name="myPageID" id="myPageID" value="${memberDTO.memberID}" readonly />
</div>
<!-- 이름 -->
<div class="field">
<label for="myPageName"></label> <input type="text" name="myPageName" id="myPageName" value="${memberDTO.name}" readonly />
</div>
<!-- 닉네임 -->
<div class="field">
<form id="changeNickname" method="post" action="changeNickname.do">
<label for="myPageNickname"></label> <input type="text" name="myPageNickname" id="myPageNickname" value="${memberDTO.nickname}" required /> <input type="submit" value="닉네임 변경" style="margin-left: 30px;" />
</form>
</div>
<!-- 번호 -->
<div class="field">
<form id="changePh" method="post" action="#">
<label for="myPagePh"></label> <input type="text" name="myPagePh" id="myPagePh" value="${memberDTO.ph}" readonly />
<button type="button" style="margin-left: 30px;" onclick="location.href='#'">전화번호 변경</button>
</form>
</div>
<!-- 자신이 작성한 글로 이동 -->
<div class="field">
<form id="myBoard" method="post" action="#">
<button type="button" onclick="location.href='myBoardSelectAllPage.do'">내 작성글로 가기</button>
</form>
</div>
<div class="field" style="display: flex; justify-content: space-between; margin-top: 30px">
<div>
<!-- "변경완료" 버튼을 눌렀을 때 firstform 제출 116번라인 upload.do 실행-->
<!-- 유저 프로필 저장만을 위해서 사용중 -->
<input type="submit" value="변경완료" form="firstForm"><br>
</div>
<div style="text-align: right;">
<button type="button" onclick="location.href='deleteAccount.do'">회원 탈퇴</button>
</div>
</div>
</div>
</section>
</div>
<script>
<!-- 전화번호 암호화 -->
// DOMContentLoaded 이벤트가 발생했을 때 실행되는 함수 화면이 다 로드됨을 뜻함
document.addEventListener('DOMContentLoaded', function () {
// myPagePh 엘리먼트를 가져와서 phoneNumberElement 변수에 저장
var phoneNumberElement = document.getElementById('myPagePh');
// phoneNumberElement이 존재하면 실행
if (phoneNumberElement) {
// myPagePh의 값을 maskPhoneNumber 함수를 이용하여 "*"로 가려진 형태로 변경
phoneNumberElement.value = maskPhoneNumber('${memberDTO.ph}');
}
});
// 전화번호의 가운데 4자리를 "*"로 암호화 하는 함수
function maskPhoneNumber(phoneNumber) {
// 전화번호를 '-'로 분리하여 parts 배열에 저장
var parts = phoneNumber.split('-');
// parts 배열의 길이가 3이면 실행
if (parts.length === 3) {
// 가운데 부분을 '****'로 대체하고 다시 '-'로 조합하여 반환
parts[1] = '****';
return parts.join('-');
} else {
// 형식에 맞지 않는 경우 그대로 반환
return phoneNumber;
}
}
<!-- MyPage들어올때 유저가 등록한 이미지를 프로필사이즈에 맞게 조정 -->
// 이미지 리사이징 여부를 나타내는 변수
// 처음 MyPage에 들어왔을 때 딱 1번 리사이징 하기 위한 변수
var imageResized = false;
// 프로필 이미지를 원하는 크기로 조정하는 함수
function resizePreviewImage(img, maxWidth, maxHeight) {
// 이미지가 리사이징되었다면 함수 종료
if (imageResized) {
return;
}
// 캔버스를 생성하고 2D 컨텍스트를 얻어옴
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 원본 이미지의 너비와 높이
var width = img.width;
var height = img.height;
// 너비와 높이 중에서 작은 쪽을 기준으로 1:1 비율로 만듦
var size = Math.min(width, height);
var xOffset = (width - size) / 2;
var yOffset = (height - size) / 2;
// 캔버스에 그림
canvas.width = size;
canvas.height = size;
ctx.drawImage(img, xOffset, yOffset, size, size, 0, 0, size, size);
// 조정된 이미지를 원하는 크기로 만들기 위한 새로운 캔버스 생성
var resizedCanvas = document.createElement('canvas');
var resizedCtx = resizedCanvas.getContext('2d');
resizedCanvas.width = maxWidth;
resizedCanvas.height = maxHeight;
// 파일 확장자에 따라 JPEG 또는 PNG로 변환하여 조정된 이미지로 설정
/* if (img.src.toLowerCase().endsWith('.png')) {
resizedCtx.drawImage(canvas, 0, 0, size, size, 0, 0, maxWidth, maxHeight);
img.src = resizedCanvas.toDataURL('image/png');
} else { */
resizedCtx.drawImage(canvas, 0, 0, size, size, 0, 0, maxWidth, maxHeight);
img.src = resizedCanvas.toDataURL('image/jpeg');
// }
// 이미지가 리사이징되었음을 표시 207번라인으로 돌아가 함수종료
imageResized = true;
}
<!-- 유저가 새로운프로필을 등록할때 선택한 사진을 프로필 사이즈에 맞게 조정하며 미리 보여줌 -->
<!-- 이후 "변경완료" 버튼을 눌러야 DB 및 mimg폴더에 저장 -->
// 화면이 모두 로드되면 실행되는 함수
$(document).ready(function () {
// 유저가 파일을 선택했을때 실행되는 이벤트 핸들러 등록
$("#fileInput").on("change", function () {
// 파일 입력과 이미지 미리보기 엘리먼트를 가져옴
var fileInput = document.getElementById('fileInput');
var preview = document.getElementById('preview');
// 파일 입력이 존재하고 첫 번째 파일이 존재하면 실행
if (fileInput.files && fileInput.files[0]) {
// FileReader 객체를 생성
var reader = new FileReader();
// 파일 읽기가 완료되면 실행되는 이벤트 핸들러 등록
reader.onload = function (e) {
// 미리보기 엘리먼트에 이미지 소스 설정
// 120번라인 id="preview" 를 가진 img태그에 파일을 읽어온 결과를 src속성에 할당
preview.src = e.target.result;
// resizeImage 함수를 사용하여 이미지 크기를 조정하고 새로운 미리보기 설정
resizeImage(fileInput.files[0], 350, 350, function (resizedImage) {
preview.src = resizedImage;
});
};
// 파일을 Data URL로 읽기 시작
// 파일의 내용을 읽어와서 Base64 인코딩된 데이터 URL로 제공
reader.readAsDataURL(fileInput.files[0]);
}
});
// 이미지 크기를 조정하고 새로운 미리보기를 설정하는 함수
function resizeImage(file, maxWidth, maxHeight, callback) {
// 이미지 객체와 FileReader 객체 생성
var img = new Image();
var reader = new FileReader();
// 파일 읽기가 완료되면 실행되는 이벤트 핸들러 등록
reader.onload = function (e) {
// 이미지 소스 설정
img.src = e.target.result;
// 이미지 로딩이 완료되면 실행되는 이벤트 핸들러 등록
img.onload = function () {
// 캔버스 생성
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 원본 이미지의 너비와 높이
var width = img.width;
var height = img.height;
// 너비와 높이 중에서 작은 쪽을 기준으로 1:1 비율로 만듦
var size = Math.min(width, height);
var xOffset = (width - size) / 2;
var yOffset = (height - size) / 2;
// 캔버스에 그림
canvas.width = size;
canvas.height = size;
ctx.drawImage(img, xOffset, yOffset, size, size, 0, 0, size, size);
// 조정된 이미지를 원하는 크기로 만들기 위한 새로운 캔버스 생성
var resizedCanvas = document.createElement('canvas');
var resizedCtx = resizedCanvas.getContext('2d');
resizedCanvas.width = maxWidth;
resizedCanvas.height = maxHeight;
// 파일 확장자에 따라 JPEG 또는 PNG로 변환하여 조정된 이미지로 설정
if (file.type.toLowerCase().includes('jpeg') || file.type.toLowerCase().includes('jpg')) {
resizedCtx.drawImage(canvas, 0, 0, size, size, 0, 0, maxWidth, maxHeight);
// 조정된 이미지를 Data URL로 변환
var resizedImage = resizedCanvas.toDataURL('image/jpeg');
// 콜백 함수 호출
// 이 코드는 콜백 함수를 호출하면서 resizedImage를 인자로 전달
// 이 콜백 함수는 전달된 이미지를 받아 특정한 작업을 수행
// 이미지를 비동기적으로 조정하고 나서 그 결과를 전달할 필요가 있을 때,
// 외부에서 지정한 콜백 함수를 호출하여 조정된 이미지를 전달
// 이렇게 하면 이미지가 비동기적으로 처리되어도 다음 단계에서 적절한 작업 가능
callback(resizedImage);
} else if (file.type.toLowerCase().includes('png')) {
resizedCtx.drawImage(canvas, 0, 0, size, size, 0, 0, maxWidth, maxHeight);
// 조정된 이미지를 Data URL로 변환
var resizedImage = resizedCanvas.toDataURL('image/png');
// 콜백 함수 호출
callback(resizedImage);
} else {
// 다른 형식의 파일은 그대로 Data URL을 콜백 함수에 전달
callback(e.target.result);
}
};
};
// 파일을 Data URL로 읽기 시작
reader.readAsDataURL(file);
}
});
</script>
</body>
</html>
선택한 이미지를 폴더에 업로드할때 원하는 파일이름명으로 변경해서 저장하고
중복된 파일명이 있다면 덮어쓰기 기능추가
ProfileUploadAction.java
package controller.common;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
import controller.front.Action;
import controller.front.ActionForward;
import model.member.MemberDAO;
import model.member.MemberDTO;
public class ProfileUploadAction implements Action {
@Override
public ActionForward execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ActionForward forward = new ActionForward();
request.setCharacterEncoding("UTF-8");
MemberDAO memberDAO = new MemberDAO();
MemberDTO memberDTO = new MemberDTO();
// UploadAction클래스 위치의 경로를 찾아서 uploadDir에 대입
// 확인된 위치 : /C:/eclipse/FinalTeamProject/workspace_infinityStone/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/chalKag/WEB-INF/classes/controller/common/
String uploadDir = this.getClass().getResource("").getPath();
// .metadata 앞까지 문자열잘라서 이미지가 저장되는 폴더인 mimg까지의 절대경로 부여
uploadDir = uploadDir.substring(1, uploadDir.indexOf(".metadata")) + "chalKag/src/main/webapp/mimg";
// 총 100M 까지 저장 가능하게 함
int maxSize = 1024 * 1024 * 100;
String encoding = "UTF-8";
// memberNum을 가져와서 사용자가 저장한 프로필 이미지 명으로 사용하려고 selectOne 사용
HttpSession session = request.getSession();
memberDTO.setMemberID((String) session.getAttribute("member"));
memberDTO.setSearchCondition("정보출력");
memberDTO = memberDAO.selectOne(memberDTO);
if (memberDTO != null) {
// 사용자가 전송한 파일 정보를 토대로 업로드 장소에 파일 업로드 수행할 수 있게 함
// 사용자가 저장한 파일 명을 원하는대로 바꿔서 원하는 폴더에 저장
// MultipartRequest 객체를 생성하여 파일 업로드 처리를 위한 정보를 설정
// request: 현재의 HttpServletRequest 객체
// uploadDir: 파일을 업로드할 디렉토리 경로
// maxSize: 업로드할 파일의 최대 크기
// encoding: 인코딩 방식
// new CustomFileRenamePolicy(Integer.toString(memberDTO.getMemberNum())):
// 파일 이름 중복 시 사용할 사용자 정의 파일 리네임 정책 객체를 생성하고 전달 103번라인 참조
// 현재 이름을 무조건 현재로그인된 memberNum으로 변경하고 저장
MultipartRequest multipartRequest = new MultipartRequest(request, uploadDir, maxSize, encoding,
new CustomFileRenamePolicy(Integer.toString(memberDTO.getMemberNum())));
// memberNum으로 재설정한 이름 newFileName에 대입
String newFileName = multipartRequest.getFilesystemName("file");
// 현재 "변경완료" 버튼을 눌러 MyPage를 빠져나갈때 선택한 프로필사진 DB 및 폴더 저장 중
// 유저가 프로필을 재선택 하지 않고 MyPage를 빠져나갈때 newFileName이 null이므로 예외처리
if (newFileName != null) {
// 유저가 프로필을 등록하지 않고 회원가입을 했을 때 아래 코드를 실행 안해도 되므로 예외처리
if (memberDTO.getProfile() != null) {
// 유저가 "티모.JPG"를 저장하고 "티모.PNG"를 저장할때 rename에 의해서
// "1.JPG"저장 후 "1.PNG"를 저장하는 과정에서 확장자를 제외한 "1"만 비교해서
// 같은게 있다면 기존 파일 삭제 후 새로운 파일 폴더에 저장하는 과정
// 기존 프로필 파일이 저장되어있는 절대경로 확인
String existingFilePath = uploadDir + File.separator + memberDTO.getProfile();
// 기존 프로필 파일의 기본 이름을 추출 (확장자 제외)
String existingFileBaseName = memberDTO.getProfile().substring(0, memberDTO.getProfile().lastIndexOf('.'));
// 새로 업로드된 파일의 기본 이름을 추출 (확장자 제외)
String newFileBaseName = newFileName.substring(0, newFileName.lastIndexOf('.'));
// 기존 프로필 파일이 저장되어있는 절대 경로를 나타내는 File 객체를 생성
File existingFile = new File(existingFilePath);
// 기존 프로필 파일이 존재하고, 새로 업로드된 파일의 기존 프로필 파일과 새로 업로드된 프로필 파일의 확장자를 제외한 이름이 같으면
if (existingFile.exists() && existingFileBaseName.equals(newFileBaseName)) {
// 기존 파일을 삭제
existingFile.delete();
}
}
//DB에 변경한 프로필 사진명 저장
memberDTO.setProfile(newFileName);
memberDTO.setSearchCondition("프로필변경");
boolean flag = memberDAO.update(memberDTO);
if (flag) {
System.out.println(memberDTO);
forward.setPath("main.do");
forward.setRedirect(true);
} else {
forward.setPath("error/alertPage.jsp");
forward.setRedirect(true);
}
// 회원가입시 저장한 회원 프로필이 없으면 그냥 메인으로 이동
} else {
forward.setPath("main.do");
forward.setRedirect(true);
}
}
return forward;
}
}
//rename Override를 위한 클래스
class CustomFileRenamePolicy extends DefaultFileRenamePolicy {
private String newFileName;
public CustomFileRenamePolicy(String newFileName) {
this.newFileName = newFileName;
}
// 저장하는 파일명 재정의 하는 메서드
@Override
public File rename(File file) {
// 파일 확장자를 extension에 대입
String extension = extractExtension(file.getName());
// newName에 newFileName(memberNum값) + ".확장자" 대입
String newName = newFileName + extension;
// 새로운 파일객체 생성 후 리턴
File newFile = new File(file.getParent(), newName);
return newFile;
}
// 파일 이름에서 확장자를 추출하는 메서드
private String extractExtension(String fileName) {
// "."이 있는 인덱스 확인
int dotIndex = fileName.lastIndexOf('.');
// 파일 이름에 점이 존재하고, 파일 이름의 마지막 문자가 점이 아닌 경우 확인
if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
// "."을포함한 문자열 추출
return fileName.substring(dotIndex);
} else {
return ""; // 확장자가 없는 경우 빈 문자열 반환
}
}
}
'코리아IT핀테크과정' 카테고리의 다른 글
검색 필터링 [TEAM PROJECT] (2) | 2024.02.02 |
---|---|
VIEW에 JSTL, EL, CustomTag 사용하기 [TEAM PROJECT] (0) | 2024.01.23 |
HandlerMapper, Factory pattern (2) | 2024.01.19 |
서블릿 Filter, Listner, XML (0) | 2024.01.18 |
JSTL, Custom tag (0) | 2024.01.17 |
Comments