

package edu.kh.project.common.util;
import java.text.SimpleDateFormat;
import java.util.Date;
// 프로그램 전체적으로 사용될 유용한 기능 모음
public class Utility {
public static int seqNum = 1; // 1 ~ 99999 번까지 1씩 증가하면서 값을 저장해줄 변수
public static String fileRename(String originalFileName) {
// 20251211100330_00001.jpg
// SimpleDateFormat : 시간을 원하는 형태의 문자열로 간단히 변경해주는 객체
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
// java.util.Date() : 현재 시간을 저장한 자바 객체
String date = sdf.format(new Date());
String number = String.format("%05d", seqNum);
seqNum++; // 1증가
if(seqNum == 100000) {
seqNum = 1;
}
// 확장자 구하기
// String.substring(인덱스);
// - 문자열을 인덱스부터 끝까지 잘라낸 결과를 반환
// String.lastIndexOf("찾을문자열")
// - 문자열에서 마지막 "찾을문자열"의 인덱스를 반환
// originalFileName == 짱구.jpg
String ext = originalFileName.substring(originalFileName.lastIndexOf("."));
// ext == .jpg
return date + "_" + number + ext;
}
}
⇒ 위 코드를 통해 파일명을 수정해준다
@PostMapping("file/test2") // /myPage/file/test1 POST 요청 매핑
public String fileUpload2(@RequestParam("uploadFile") MultipartFile uploadFile,
@SessionAttribute("loginMember") Member loginMember, RedirectAttributes ra) {
try {
int memberNo = loginMember.getMemberNo();
int result = service.fileUpload2(uploadFile, memberNo);
String message = null;
if (result > 0) {
message = "파일 업로드 성공";
} else {
message = "파일 업로드 실패";
}
ra.addFlashAttribute("message", message);
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:/myPage/fileTest";
}
@Override
public int fileUpload2(MultipartFile uploadFile, int memberNo) throws Exception {
// MultipartFile이 제공하는 메소드
// - isEmpty() : 업로드된 파일이 없을경우 true / 있다면 false
// - getSize() : 파일 크기
// - getOriginalFileName() : 원본 파일명
// - transferTo(경로) :
// 메모리 또는 임시 저장 경로에 업로드된 파일을
// 업로드된 파일이 없다면
if (uploadFile.isEmpty()) {
return 0;
}
// 업로드된 파일이 있다면
// 1. 서버에 저장될 서버 폴더 경로 만들기
// 파일이 저장될 서버 폴더 경로
String folderPath = "C:/uploadFiles/test/";
// 클라이언트가 파일이 저장된 폴더에 접근할 수 있는 주소(요청 주소)
String webPath = "/myPage/file/";
// 2. DB에 전달할 데이터를 DTO로 묶어서 INSERT
// webPath, memberNo, 원본파일명, 변경된파일명
String fileRename = Utility.fileRename(uploadFile.getOriginalFilename());
// Builder 패턴을 이용해서 UploadFile 객체 생성
// 장점 1) 반복되는 참조변수명, set 구문 생략
// 장점 2) method chaining을 이용하여 한줄로 작성 가능
UploadFile uf = UploadFile.builder().memberNo(memberNo).filePath(webPath)
.fileOriginalName(uploadFile.getOriginalFilename()).fileRename(fileRename).build();
int result = mapper.insertUploadFile(uf);
// 3. 삽입 (INSERT) 성공 시 파일을 지정된 폴더에 저장
// 삽입 실패 시
if (result == 0)
return 0;
// 삽입 성공 시
// C:/uploadFiles/test/변경된파일명으로
// 파일을 서버 컴퓨터에 저장!
uploadFile.transferTo(new File(folderPath + fileRename));
// C:/uploadFiles/test/20251211100330_00001.jpg
return result;
}


⇒ 서버 컴퓨터와 DB에 모두 저장된 것을 확인할 수 있다.

<input type="file" name="bbb" multiple />
⇒ 여러 파일을 선택할 수 있다.
@PostMapping("file/test3")
public String fileUpload3(@RequestParam("aaa") List<MultipartFile> aaaList,
@RequestParam("bbb") List<MultipartFile> bbbList, @SessionAttribute("loginMember") Member loginMember,
RedirectAttributes ra) throws Exception {
// aaa 파일 미제출 시
// 0번, 1번 인덱스로 구성되어있음 - 파일은 모두 비어있음
// log.info("aaaList = " + aaaList); // [요소, 요소]
// bbb(multiple) 파일 미제출 시
// 0번 인덱스로 구성 - 파일이 비어있음
// log.info("bbbList = " + bbbList); // [요소]
// 여러 파일 업로드 할 수 있는 서비스 호출할 것
int result = service.fileUpload3(aaaList, bbbList, loginMember.getMemberNo());
// result == aaaList와 bbbList에 업로드된 파일 갯수
String message = null;
if (result == 0) {
message = "업로드된 파일이 없습니다";
} else {
message = result + "개의 파일이 업로드 되었습니다!";
}
ra.addFlashAttribute("message", message);
return "redirect:/myPage/fileTest";
}
@Override
public int fileUpload3(List<MultipartFile> aaaList, List<MultipartFile> bbbList, int memberNo) throws Exception {
// 1. aaaList 처리
int result1 = 0;
// 업로드된 파일이 없을 경우를 제외하고 업로드
for (MultipartFile file : aaaList) {
if (file.isEmpty())
// 파일이 없으면 다음 파일로 검사하러 가기
continue; // 아래 코드 수행 X 다음 반복으로 넘어감...
// fileUpload2() 메서드 호출(재활용)
// -> 파일 하나 업로드 + DB INSERT
result1 += fileUpload2(file, memberNo);
}
// 2. bbbList 처리
int result2 = 0;
// 업로드된 파일이 없을 경우를 제외하고 업로드
for (MultipartFile file : bbbList) {
if (file.isEmpty())
// 파일이 없으면 다음 파일로 검사하러 가기
continue; // 아래 코드 수행 X 다음 반복으로 넘어감...
// fileUpload2() 메서드 호출(재활용)
// -> 파일 하나 업로드 + DB INSERT
result2 += fileUpload2(file, memberNo);
}
return result1 + result2;
}


[input type="file" 사용 시 유의 사항]
1. 파일 선택 후 취소를 누르면
선택한 파일이 사라진다 (value == '')
2. value로 대입할 수 있는 값은 '' (빈칸)만 가능하다
3. 선택된 파일 정보를 저장하는 속성은
value가 아니라 files이다
config.properties에 요청경로/서버저장경로
프로필이미지
/myPage/profile/ ↔ C:/uploadFIles/profile/
@PostMapping("profile") // /myPage/profile POST 요청 매핑
public String profile(@RequestParam("profileImg") MultipartFile profileImg, RedirectAttributes ra,
@SessionAttribute("loginMember") Member loginMember) throws Exception {
// 서비스 호출
int result = service.profile(profileImg, loginMember);
String message = null;
if (result > 0) {
message = "변경 성공";
} else {
message = "변경 실패...";
}
ra.addFlashAttribute("message", message);
return "redirect:profile"; // 리다이렉트 - /myPage/profile GET 요청
}
@Override
public int profile(MultipartFile profileImg, Member loginMember) throws Exception {
// 프로필 이미지 경로(수정할 경로)
String updatePath = null;
// 변경명 저장
String rename = null;
// 업로드한 이미지가 있을 경우
if (!profileImg.isEmpty()) {
// updatePath 경로 조합
// filePath, fileRename을 조합해서 member에 넣을 것
// 1. 파일명 변경
rename = Utility.fileRename(profileImg.getOriginalFilename());
// 2. /myPage/profile/변경된파일명
updatePath = profileWebPath + rename;
}
// 수정된 프로필 이미지 경로와 어떤 회원의 프로필을 수정할 것인지 알아야함
// => 즉, 수정된 프로필 이미지 경로 + 회원 번호를 저장할 DTO 객체를 보내줄 것
Member member = Member.builder().memberNo(loginMember.getMemberNo()).profileImg(updatePath).build();
// UPDATE 수행
int result = mapper.profile(member);
if (result > 0) { // DB에 업데이트 성공
// 프로필 이미지를 없앤 경우(NULL로 수정한 경우를 의미)를 제외한 경우
// -> 즉, 업로드한 이미지가 있을 경우
if (!profileImg.isEmpty()) {
// 파일을 서버 지정된 폴더에 저장
profileImg.transferTo(new File(profileFolderPath + rename));
// C:/uploadFiles/profile/변경한이름
}
// 세션에 등록된 현재 로그인한 회원 정보에서
// 프로필 이미지 경로를 DB에 업데이트한 경로로 변경
loginMember.setProfileImg(updatePath);
}
return result;
}
