728x90
반응형
npm install cropper
npm install jquery-cropper
$("#profile-image-file").change(function(e) { if (e.target.files.length === 1) { const reader = new FileReader();
reader.onload = e => { if (e.target.result) { let img = document.createElement("img");
img.id = 'new-profile';
img.src = e.target.result;
img.width = 250;
$newProfileImage.html(img);
$newProfileImage.show();
$currentProfileImage.hide();
let $newImage = $(img);
$newImage.cropper({aspectRatio: 1});
cropper = $newImage.data('cropper');
$cutBtn.show();
$confirmBtn.hide();
$resetBtn.show();
} };
reader.readAsDataURL(e.target.files[0]);
} });
https://getbootstrap.com/docs/5.0/components/card/
이미지 변경 카드 뷰
<!-- 프로필 이미지 -> hidden값으로 사용자가 직접 입력하지 않고, cropper.js를 이용해 이미지 크기 설정 -->
<div class="col-sm-6">
<div class="card text-center">
<div class="card-header">프로필 이미지</div>
<div id="current-profile-image" class="mt-3">
<svg th:if="${#strings.isEmpty(profile.profileImage)}"
class="rounded" th:data-jdenticon-value="${account.nickname}"
width="125" height="125"></svg>
<img th:if="${!#strings.isEmpty(profile.profileImage)}"
class="rounded" th:src="${profile.profileImage}" width="125"
height="125" alt="name" th:alt="${account.nickname}" />
</div>
<div id="new-profile-image" class="mt-3"></div>
<div class="card-body">
<div class="custom-file">
<input type="file" class="custom-file-input"
id="profile-image-file"> <label
class="custom-file-label" for="profile-image-file">프로필
이미지 변경</label>
</div>
<div id="new-profile-image-control" class="mt-3">
<button class="btn btn-outline-primary btn-block"
id="cut-button">자르기</button>
<button class="btn btn-outline-success btn-block"
id="confirm-button">확인</button>
<button class="btn btn-outline-warning btn-block"
id="reset-button">취소</button>
</div>
<div id="cropped-new-profile-image" class="mt-3"></div>
</div>
</div>
</div>
이미지를 문자열로 저장하는 방법
DataURL
● data: 라는 접두어를 가진 URL로 파일을 문서에 내장 시킬때 사용할 수 있다.
● 이미지를 DataURL로 저장할 수 있다.
Profile과 AccountService에 profileImage추가
Profile
package com.demo.settings;
import com.demo.domain.Account;
import lombok.Data;
import lombok.NoArgsConstructor;
//폼을 채울 객체 생성
//롬복을 이용한 getter, setter 자동생성
@Data
//참조받을 account를 위해 기본 생성자를 만들어주는 어노테이션은 사용한다
@NoArgsConstructor
public class Profile {
private String bio;
private String url;
private String occupation;
private String location;
//이미지
private String profileImage;
//@NoArgsConstructor를 대신해 사용한다면
//public Profile(){};
//프로필 폼에 채울 객체에 정보 넣기 -> ModelMapper를 이용해 생성
public Profile(Account account) {
this.bio=account.getBio();
this.url=account.getUrl();
this.occupation=account.getOccupation();
this.location=account.getLocation();
this.profileImage=account.getProfileImage();
}
}
AccountService의 updateProfile메서드
public void updateProfile(Account account, Profile profile) {
// TODO Auto-generated method stub
account.setUrl(profile.getUrl());
account.setOccupation(profile.getOccupation());
account.setLocation(profile.getLocation());
account.setBio(profile.getBio());
account.setProfileImage(profile.getProfileImage());
//트랜잭션이 끝난 detached상태이기 때문에 직접 DB에 반영을 해준다.
//즉 id값이 있으면 merge를 수행한다.
accountRepository.save(account);
}
profile
<!-- 프로필 값 -->
<div class="form-group">
<input id="profileImage" type="hidden" th:field="*{profileImage}"
class="form-control" />
</div>
자바스크립트 cropper
https://fengyuanchen.github.io/cropperjs/
자바스크립트를 이용한, 이미지 변경하기 & Cropper를 이용해 자르기
<!-- cropper 스크립트 가져오기 -->
<link href="/node_modules/cropper/dist/cropper.min.css"
rel="stylesheet">
<script src="/node_modules/cropper/dist/cropper.min.js"></script>
<script src="/node_modules/jquery-cropper/dist/jquery-cropper.min.js"></script>
<!-- 이미지 변경 jquery -->
<script type="application/javascript">
$(function() {
<!-- 버튼 만들기-->
cropper = '';
let $confirmBtn = $("#confirm-button");
let $resetBtn = $("#reset-button");
let $cutBtn = $("#cut-button");
let $newProfileImage = $("#new-profile-image");
let $currentProfileImage = $("#current-profile-image");
let $resultImage = $("#cropped-new-profile-image");
let $profileImage = $("#profileImage");
<!-- 필요 없는 영역과 버튼 숨기기-->
$newProfileImage.hide();
$cutBtn.hide();
$resetBtn.hide();
$confirmBtn.hide();
<!-- 프로필 이미지 선택의 값이 바뀌면-->
$("#profile-image-file").change(function(e) {
<!-- 파일리더 변수를 통해 파일을 읽어온다 -->
if (e.target.files.length === 1) {
const reader = new FileReader();
<!-- 파일을 읽어왔으면 -->
reader.onload = e => {
<!-- target을 가져온다.-->
if (e.target.result) {
<!-- 이미지 파일을 선택하지 않았을 때-->
if (!e.target.result.startsWith("data:image")) {
alert("이미지 파일을 선택하세요.");
return;
}
<!-- 이미지 파일 선택했을 때 -->
<!-- 이미지 태그를 만들어서 가져온 이미지를 채워놓고 값들을 채워놓고, 필요한 부분은 보여주고, 필요없는 부분은 숨긴다.-->
let img = document.createElement("img");
img.id = 'new-profile';
img.src = e.target.result;
img.setAttribute('width', '100%');
$newProfileImage.html(img);
$newProfileImage.show();
$currentProfileImage.hide();
<!-- 현재 이미지 숨기고 새로운 이미지를 보여준다.-->
<!--새이미지를 제이쿼리로 감싸서 크루퍼를 적용한다.-->
let $newImage = $(img);
$newImage.cropper({aspectRatio: 1});
cropper = $newImage.data('cropper');
<!-- 새로운 이미지에 크루퍼가 적용이 되었으면 잘라내기 버튼, 리셋버튼 보여주고, 확인버튼은 숨긴다.-->
$cutBtn.show();
$confirmBtn.hide();
$resetBtn.show();
}
};
reader.readAsDataURL(e.target.files[0]);
<!-- -->
}
});
<!-- 리셋버튼 클릭시, 최종적으로 프로필이미지 값에 비어있는 값을 채워놓는다.-->
$resetBtn.click(function() {
$currentProfileImage.show();
$newProfileImage.hide();
$resultImage.hide();
$resetBtn.hide();
$cutBtn.hide();
$confirmBtn.hide();
$profileImage.val('');
});
<!-- 잘라내기 버튼 클릭시-->
$cutBtn.click(function () {
let dataUrl = cropper.getCroppedCanvas().toDataURL();
if (dataUrl.length > 1000 * 1024) {
alert("이미지 파일이 너무 큽니다. 1024000 보다 작은 파일을 사용하세요. 현재 이미지 사이즈 " + dataUrl.length);
return;
}
<!-- 잘라낼 이미지 만큼만 자른다.-->
let newImage = document.createElement("img");
newImage.id = "cropped-new-profile-image";
newImage.src = dataUrl;
newImage.width = 125;
$resultImage.html(newImage);
$resultImage.show();
$confirmBtn.show();
$confirmBtn.click(function () {
$newProfileImage.html(newImage);
$cutBtn.hide();
$confirmBtn.hide();
$profileImage.val(dataUrl);
});
});
});
</script>
변경을 할 경우 메인 네비게이션바에 바로 적용이 되지 않는다.
-> 유저가 가지고있는 데이터를 반영하는 것이 아니기 때문에 -> 인증 정보의 이름으로 먼저 처리를 하기 때문에
fragments
<li class="nav-item dropdown" sec:authorize="isAuthenticated()">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
aria-expanded="false">
<!-- jenticon을 이용한 프로필 아이콘, 타임리프를 이용해 스프링시큐리티 표현식으로 닉네임-->
<svg data-jdenticon-value="user127" th:data-jdenticon-value="${#authentication.name}" width="24" height="24" class="rounded border bg-light"></svg>
</a>
<div class="dropdown-menu dropdown-menu-sm-right" aria-labelledby="userDropdown">
<ul>
<h6 class="dropdown-header">
<span sec:authentication="name">Username</span>
</h6>
<a class="dropdown-item" th:href="@{'/profile/' + ${#authentication.name}}">프로필</a>
<a class="dropdown-item">스터디</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" th:href="@{'/settings/profile'}">설정</a>
<form class="form-inline my-2 my-lg-0" action="#" th:action="@{/logout}" method="post">
<button class="dropdown-item" type="submit">로그아웃</button>
</form>
</ul>
</div>
</li>
변경 후
<li class="nav-item dropdown" sec:authorize="isAuthenticated()">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
aria-expanded="false">
<!-- jenticon을 이용한 프로필 아이콘, 타임리프를 이용해 스프링시큐리티 표현식으로 닉네임-->
<!-- 이미지 변경해도 반영할 수 있도록 변경-->
<svg th:if="${#strings.isEmpty(account?.profileImage)}" th:data-jdenticon-value="${#authentication.name}"
width="24" height="24" class="rounded border bg-light"></svg>
<img th:if="${!#strings.isEmpty(account?.profileImage)}" th:src="${account.profileImage}"
width="24" height="24" class="rounded border"/>
</a>
<div class="dropdown-menu dropdown-menu-sm-right" aria-labelledby="userDropdown">
<ul>
<h6 class="dropdown-header">
<span sec:authentication="name">Username</span>
</h6>
<a class="dropdown-item" th:href="@{'/profile/' + ${#authentication.name}}">프로필</a>
<a class="dropdown-item">스터디</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" th:href="@{'/settings/profile'}">설정</a>
<form class="form-inline my-2 my-lg-0" action="#" th:action="@{/logout}" method="post">
<button class="dropdown-item" type="submit">로그아웃</button>
</form>
</ul>
</div>
</li>
728x90
반응형
'Server Programming > Spring Boot Full-Stack Programming' 카테고리의 다른 글
[스프링 풀스택 클론 코딩 - 계정 설정] (2-7) 알림 설정 (0) | 2022.09.05 |
---|---|
[스프링 풀스택 클론 코딩 - 계정 설정] (2-5) 패스워드 수정 (0) | 2022.09.02 |
[스프링 풀스택 클론 코딩] 엔티티 상태에 따른 DB 반영 (0) | 2022.09.01 |
[스프링 풀스택 클론 코딩 - 계정 설정] (2-2) 프로필 수정 처리 (0) | 2022.09.01 |
[스프링 풀스택 클론 코딩 - 계정 설정] (2-1) 프로필 수정 폼 (0) | 2022.09.01 |