이번에는 유저의 프로필 사진을 저장하기 위한
UserFile
엔티티 관련 로직을 알아보도록 하자.
먼저, 유저의 프로필사진을 저장하기 위한 폴더를 Sekkison 폴더 바로 밑에 만들어 주었다.
이후application.properties
세팅을 해 준다. ( 게시글의 처음부터 따라왔다면 되어 있을 것이다.)application.properties
spring.servlet.multipart.max-file-size=5MB spring.servlet.multipart.max-request-size=20MB file.upload-dir=upload app.upload.path=upload
업로드 파일의 경로를
upload
로 하고, 파일의 최대 크기를5MB
로 지정하였다.UserFileController
@RestController @RequestMapping("/userFiles") @RequiredArgsConstructor public class UserFileController { private final UserFileService userFileService; // 파일저장 경로 @Value("${file.upload-dir}") private String uploadDir; // 프로필 업로드 @ResponseBody @PostMapping("/upload") public ResponseForm uploadFile( @RequestParam("file") MultipartFile file, @RequestParam("userId") Long userId) { try { return userFileService.uploadFile(userId, file); } catch (IOException e) { return null; } } }
컨트롤러에서는
userFiles/upload
라는 요청에 프로필 업로드 함수를 실행시킨다.
userId
와MultipartFile
을 받아 해당 유저의 프로필 사진을 설정하는 것이다.
@Value
를 통해application.properties
에서 설정한 파일 업로드 경로를 가져올 수 있다.UserFileService
@Service @Transactional @RequiredArgsConstructor public class UserFileService { // 파일 경로 @Value("${file.upload-dir}") private String uploadDir; // 이미지 파일은 최대 5MB private static final long MAX_IMAGE_SIZE = 5242880; private final UserFileRepository userFileRepository; // 프로필 올리기 public ResponseForm uploadFile(Long userId, MultipartFile file) throws IOException { ResponseForm responseForm = new ResponseForm(); // 파일이 없거나 빈파일이면 레파지토리에서 userId 기준으로 탐색 if (file.isEmpty() || "".equals(file.getName())) { UserFile userFile = userFileRepository.findByUserId(userId).orElse(null); // 파일이 없고 기본 파일이 아니면 파일 삭제 후 기본 파일로 저장 if (userFile != null && !userFile.getFile().equals("default.jpg")) new File(uploadDir + "/" + userFile.getFile()).delete(); userFile.setFile("default.jpg"); userFileRepository.save(userFile); return responseForm.setSuccess(null); } // 이미지 크기 제한 체크 if (file.getSize() > MAX_IMAGE_SIZE) { throw new RuntimeException("이미지 크기가 너무 큽니다."); } // 이미지 저장 String fileName = writeFile(file); UserFile userFile = userFileRepository.findByUserId(userId).orElse(null); // 파일이 없고 기본 파일이 아니면 저장 if (userFile != null && !userFile.getFile().equals("default.jpg")) new File(uploadDir + "/" + userFile.getFile()).delete(); userFile.setFile(fileName); userFileRepository.save(userFile); return responseForm.setSuccess(null); } private String writeFile(MultipartFile file) throws IOException { String extension = com.google.common.io.Files.getFileExtension(file.getOriginalFilename()); String fileName = UUID.randomUUID().toString() + "." + extension; byte[] fileContent = file.getBytes(); String filePath = uploadDir + "/" + fileName; Path path = Paths.get(filePath); Files.write(path, fileContent); return fileName; } }
자바에서 자체적으로 제공하는
UUID
클래스를 사용하여 이미지의 이름을 변경하고 적용시킨 후 로컬 파일 시스템에 저장하였다.
( 같은 이름의 이미지 파일을 한 폴더 안에 넣지 못하기 때문 )
조금 더 자세한 설명을 위해 추가 설명하겠다.
- 먼저, 업로드된 파일의 확장자를 가져와서 변수
extension
에 저장한다. 이를 위해Google Guava
라이브러리의Files
클래스의getFileExtension
메서드를 사용하였다.
- 사용자가 업로드한 파일이 이름이 기본 프로필 파일과 이름이 같거나 다른 유저의 파일과 같을 경우 문제가 생기기 때문에
UUID
를 사용하여 랜덤한 파일 이름을 생성하였다.
- 업로드된 파일의 내용을
byte
배열로 가져와fileContent
변수에 저장한다.
- 로컬 파일 시스템에 파일을 저장하기 위해, 저장할 파일의 경로를
filePath
변수에 저장한다. 이는uploadDir
변수에 파일 이름을 더해 경로를 만들어주게 된다.
Path path = Paths.get(filePath);
이후
Files.write(path, fileContent);
를 통해 파일을 저장한다.
Sekkison에서 유저가 파일을 올리지 않으면 기본 이미지로 표현되기 때문에upload
폴더에 기본 이미지를 넣어주고,userfile
객체가null
일 경우 이미지를 넣어주었다.따라서
userfile
이null
이 아니면서 기본이미지가 아닌 경우 유저가 원하는 사진을 넣으면 프로필 사진이 변경된다.
코드를 거의 그대로 작성하였으니, 이 게시글을 따라
Controller
,Service
를 만든다면 파일 업로드를 간단하게 구현할 수 있을 것이다.