Wanna be Brilliant Full-Stack Developer
2/17 SPringBoot 프로필 페이지 유저 사진 변경! 본문
목표
프로필 사진 등록!
1. PageUserId, pincipalId를 비교해서 같을 때만 동작하기
2. 이미지를 put방식(ajax)으로 서버로 전송하기
- FormData 객체 이용
// (3) 유저 프로파일 사진 변경 (완)
function profileImageUpload() {
$("#userProfileImageInput").click();
$("#userProfileImageInput").on("change", (e) => {
let f = e.target.files[0];
if (!f.type.match("image.*")) {
alert("이미지를 등록해야 합니다.");
return;
}
// 사진 전송 성공시 이미지 변경
let reader = new FileReader();
reader.onload = (e) => {
$("#userProfileImage").attr("src", e.target.result);
}
reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
});
}
// (4) 사용자 정보 메뉴 열기 닫기
function popup(obj) {
$(obj).css("display", "flex");
}
function closePopup(obj) {
$(obj).css("display", "none");
}
// (5) 사용자 정보(회원정보, 로그아웃, 닫기) 모달
function modalInfo() {
$(".modal-info").css("display", "none");
}
// (6) 사용자 프로파일 이미지 메뉴(사진업로드, 취소) 모달
function modalImage() {
$(".modal-image").css("display", "none");
}
여기서부터 변경을 시작하려고한다!
// (3) 유저 프로파일 사진 변경 (완)
function profileImageUpload (pageUserId, principalId) {
//console.log("pageUserId",pageUserId);
//console.log("principalId",principalId);
if(pageUserId != principalId) {
alert("프로필 사진을 수정할 수 없는 유저입니다.");
return;
}
$("#userProfileImageInput").click();
$("#userProfileImageInput").on("change", (e) => {
let f = e.target.files[0];
if (!f.type.match("image.*")) {
alert("이미지를 등록해야 합니다.");
return;
}
// 사진 전송 성공시 이미지 변경
let reader = new FileReader();
reader.onload = (e) => {
$("#userProfileImage").attr("src", e.target.result);
}
reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
});
}
pageUserId와 principalId를 비교해서 같지 아니하면 프로필 사진을 수정할수 없도록 만들어놨다.
이제 같은 유저인 경우 서버에 이미지를 전송하는 코드를 만들어보려고한다
이 이미지가 어디에 있냐면
<form id="userProfileImageForm">
<input type="file" name="profileImageFile" style="display: none;"
id="userProfileImageInput" />
</form>
이 form 태그에 있는것을 애를 가지고 와야하는데 가져와야하는 방법이 간단한데
사진을 전송할것이니까 데이터 전송타입이 ENC타입이 MultipartForm데이터 타입이 되어야한다!
// 서버에 이미지를 전송
let profileImageForm = $("#userProfileImageForm")[0];
console.log(profileImageForm);
이제 form 데이터를 전송하려면 formData 객체로 받아야한다.
여기서 말하는 formData는 어떤 데이터를 말하는것이냐면
FormData 객체를 이용하면 form태그의 필드와 그 값을 나타내는 일련의 key/value 쌍을 담을 수 있다.
// (3) 유저 프로파일 사진 변경 (완)
function profileImageUpload(pageUserId, principalId) {
//console.log("pageUserId",pageUserId);
//console.log("principalId",principalId);
if (pageUserId != principalId) {
alert("프로필 사진을 수정할 수 없는 유저입니다.");
return;
}
$("#userProfileImageInput").click();
$("#userProfileImageInput").on("change", (e) => {
let f = e.target.files[0];
if (!f.type.match("image.*")) {
alert("이미지를 등록해야 합니다.");
return;
}
// 서버에 이미지를 전송
let profileImageForm = $("#userProfileImageForm")[0];
console.log(profileImageForm);
// FormData 객체를 이용하면 form태그의 필드와 그 값을 나타내는 일련의 key/value 쌍을 담을 수 있다.
let formData = new FormData(profileImageForm);
$.ajax({
type: "put",
url: `/api/user/${principalId}/profileImageUrl`,
data: fromData,
contentType: false, //필수 : x-www-form-urlencoded로 파싱되는 것을 방지
processData: false, // 필수: contentType을 false를 줬을떄 QueryString자동설정됨. 해제
enctype: "multipart/form-data",
dataType: "json"
}).done(res => {
// 사진 전송 성공시 이미지 변경
let reader = new FileReader();
reader.onload = (e) => {
$("#userProfileImage").attr("src", e.target.result);
}
reader.readAsDataURL(f); // 이 코드 실행시 reader.onload 실행됨.
}).fail(error => {
console.log("오류",error);
})
});
}
// (4) 사용자 정보 메뉴 열기 닫기
function popup(obj) {
$(obj).css("display", "flex");
}
function closePopup(obj) {
$(obj).css("display", "none");
}
// (5) 사용자 정보(회원정보, 로그아웃, 닫기) 모달
function modalInfo() {
$(".modal-info").css("display", "none");
}
// (6) 사용자 프로파일 이미지 메뉴(사진업로드, 취소) 모달
function modalImage() {
$(".modal-image").css("display", "none");
}
// (7) 구독자 정보 모달 닫기
function modalClose() {
$(".modal-subscribe").css("display", "none");
location.reload();
}
그러면 이제 API만 만들면된다!
@PutMapping("/api/user/{principalId}/profileImageUrl")
public ResponseEntity<?> profileImageUrlUpdate(@PathVariable int principalId, MultipartFile file) {
} 변수 이미지를 정하는것이 중요한데
@PutMapping("/api/user/{principalId}/profileImageUrl")
public ResponseEntity profileImageUrlUpdate(@PathVariable int principalId, MultipartFile profileImageFile {
}
@PutMapping("/api/user/{principalId}/profileImageUrl")
public ResponseEntity<?> profileImageUrlUpdate(@PathVariable int principalId, MultipartFile profileImageFile,
@AuthenticationPrincipal PrincipalDetails principalDetails) {
User userEntity = userService.회원프로필사진변경(principalId,profileImageFile);
principalDetails.setUser(userEntity); //세션변경
return new ResponseEntity<>(new CMRespDto<>(1, "프로필사진변경 성공",null),HttpStatus.OK);
}
이렇게 완성하고 회원프로필사진변경을 해야하는데 유저서비스에가서
package com.cos.photogramstart.service;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.function.Supplier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import com.cos.photogramstart.domain.subscribe.SubscribeRepository;
import com.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import com.cos.photogramstart.handler.ex.CustomApiException;
import com.cos.photogramstart.handler.ex.CustomException;
import com.cos.photogramstart.handler.ex.CustomValidationApiException;
import com.cos.photogramstart.web.dto.user.UserProfileDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
private final SubscribeRepository subscribeRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Value("${file.path}")
private String uploadFolder;
@Transactional
public User 회원프로필사진변경(int principalId, MultipartFile profileImageFile) {
UUID uuid = UUID.randomUUID(); // uuid
String imageFileName = uuid+ "_" + profileImageFile.getOriginalFilename(); // 1.jpg
System.out.println("이미지 파일이름: "+ imageFileName);
Path imageFilePath = Paths.get(uploadFolder+imageFileName);
//통신, I/O -> 예외
try {
Files.write(imageFilePath, profileImageFile.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
User userEntity = userRepository.findById(principalId).orElseThrow(()->{
throw new CustomApiException("유저를 찾을 수 없습니다.");
});
userEntity.setProfileImageUrl(imageFileName);
return userEntity;
} // 더티체킹으로 업데이트됨
@Transactional(readOnly = true)
public UserProfileDto 회원프로필(int pageUserId, int principalId) {
UserProfileDto dto = new UserProfileDto();
//SELECT * FROM image WHERE userId = :userId;
User userEntity = userRepository.findById(pageUserId).orElseThrow(()-> {
throw new CustomException("해당 프로필 페이지는 없는 페이지입니다.");
});
dto.setUser(userEntity);
dto.setPageOwnerState(pageUserId == principalId);
dto.setImageCount(userEntity.getImages().size());
int subscribeState = subscribeRepository.mSubscribeState(principalId, pageUserId);
int subscribeCount = subscribeRepository.mSubscribeCount(pageUserId);
dto.setSubscribeState(subscribeState == 1);
dto.setSubscribeCount(subscribeCount);
//좋아요 카운트 추가하기
userEntity.getImages().forEach((image)->{
image.setLikeCount(image.getLikes().size());
});
return dto;
}
@Transactional
public User 회원수정(int id, User user ) {
//1. 영속화
//1. 무조건 찾았다. 걱정마 get() 2. 못찾았어 익섹션 발통시킬때 orElseThrow()
User userEntity = userRepository.findById(id).orElseThrow(() -> {return new CustomValidationApiException("찾을 수 없는 id입니다."); });
//2. 영속화된 오브젝트를 수정 - 수정이 완료되면 더티체킹 ( 업데이트 완료)
userEntity.setName(user.getName());
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
userEntity.setPassword(encPassword);
userEntity.setBio(user.getBio());
userEntity.setWebsite(user.getWebsite());
userEntity.setPhone(user.getPhone());
userEntity.setGender(user.getGender());
return userEntity;
}//더티체킹이 일어나서 업데이트가 완료됨.
}
<img class="profile-image" src="/upload/${dto.user.profileImageUrl}"
onerror="this.src='/images/person.jpeg'" id="userProfileImage" />
form태그 데이터(key = value) 를 전송할떄는 serialize를 사용하면되고 사진전송할때는 formData로 하면된다.
'Back-End > Spring Boot' 카테고리의 다른 글
2/21 SpringBoot 뷰 렌더링 완료하기! (0) | 2022.02.21 |
---|---|
2/18 SpringBoot Comment 모델 만들기 (0) | 2022.02.18 |
2/17 SpringBoot 인기 페이지 & 프로필 Likes 카운트 구현 (0) | 2022.02.17 |
2/17 SpringBoot Likes View Rendering (0) | 2022.02.17 |
2/16 Spring OOP관점에서 모델링이란??? (0) | 2022.02.17 |