동기방식 파일업로드 처리 기본
UploadController
- 파일 업로드의 경로와 jsp파일 처리, 파일명을 중복 없는 파일명으로 변경하여 파일 업로드 처리
- 파일이 여러개면 List로 처리해서 반복문을 통해 해결할 수 있다.
@Controller
@Slf4j
public class UploadController {
private String rootPath = "C:/Users/smr78/OneDrive/바탕 화면/yocong/spring-prj/upload";
@GetMapping("/upload/form")
public String uploadForm() {
return "upload/upload-form";
}
@PostMapping("/upload/file")
public String uploadFile(
@RequestParam("thumbnail") MultipartFile file
) {
log.info("file-name: {}", file.getOriginalFilename());
log.info("file-size: {}MB", file.getSize() / 1024.0 / 1024.0);
log.info("file-type: {}", file.getContentType());
File root = new File(rootPath);
if (!root.exists()) root.mkdirs();
FileUtil.uploadFile(rootPath, file);
return "redirect:/upload/form";
}
}
- 서버에서 전송된 파일의 정보를 잘 읽어오는 것을 볼 수 있음
FileUtil
public class FileUtil {
public static String uploadFile(String rootPath, MultipartFile file) {
String newFileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
try {
file.transferTo(new File(rootPath, newFileName));
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
- 중복없는 파일명이 생성된 것을 볼 수 있다.
- thumbnail이라는 name을 Controller에서 file로 불러오는 것임
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Web Study</title>
</head>
<body>
<h1>파일 업로드 예제</h1>
<form action="/upload/file" method="post" enctype="multipart/form-data">
<%-- form태그의 enctype="multipart/form-data" 속성이 있어야 한다. --%>
<input type="file" name="thumbnail" id="img-input" accept="image/*">
<button type="submit">전송</button>
</form>
</body>
</html>
FileUtil - 날짜별로 관리하기 위해 날짜 폴더 생성
public class FileUtil {
public static String uploadFile(String rootPath, MultipartFile file) {
String newFileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
String newUploadPath = makeDateFormatDirectory(rootPath);
try {
file.transferTo(new File(newUploadPath, newFileName));
} catch (IOException e) {
e.printStackTrace();
}
String fullPath = newUploadPath + "/" + newFileName;
String urlPath = "/local"+fullPath.substring(rootPath.length());
return urlPath;
}
private static String makeDateFormatDirectory(String rootPath) {
LocalDate now = LocalDate.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
List<String> dateList = List.of(year + "", len2(month), len2(day));
String newDirectoryPath = rootPath;
for (String s : dateList) {
newDirectoryPath += "/" + s;
File f = new File(newDirectoryPath);
if (!f.exists()) f.mkdir();
}
return newDirectoryPath;
}
private static String len2(int n) {
return new DecimalFormat("00").format(n);
}
}
- 파일업로드의 input을 꾸미는 것보다 기존 input 태그를 숨기고 새로운 태그를 만들어서 꾸미고 input에 접근할 수 있게 만드는 것이 꾸밀 때 편하다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %> <%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Web Study</title>
<style>
#img-input {
display: none;
}
.upload-box {
width: 150px;
height: 150px;
border: 3px dashed orange;
display: flex;
justify-content: center;
align-items: center;
color: red;
font-weight: 700;
cursor: pointer;
}
</style>
</head>
<body>
<h1>파일 업로드 예제</h1>
<div class="upload-box">여기를 눌러 파일을 올려주세요</div>
<form action="/upload/file" method="post" enctype="multipart/form-data">
<%-- form태그의 enctype="multipart/form-data" 속성이 있어야 한다. --%>
<input type="file" name="thumbnail" id="img-input" accept="image/*" />
<button type="submit">전송</button>
</form>
<script>
document.querySelector('.upload-box').onclick = e => {
document.getElementById('img-input').click();
};
</script>
</body>
</html>
클라이언트 부분에서의 회원가입시 이미지 처리
sign-up.jsp
- MultipartFile을 사용하면
enctype="multipart/form-data"
코드 필수
- 회원가입시 프로필 이미지를 추가할 수 있도록함
<div class="card-body">
<form
action="/members/sign-up"
name="signup"
id="signUpForm"
method="post"
enctype="multipart/form-data"
>
<div class="profile">
<div class="thumbnail-box">
<img src="/assets/img/image-add.png" alt="프로필 썸네일">
</div>
<label>프로필 이미지 추가</label>
<input
type="file"
id="profile-img"
accept="image/*"
style="display: none;"
name="profileImage"
>
</div>
<script>
const $profile = document.querySelector('.profile');
const $fileInput = document.getElementById('profile-img');
$profile.addEventListener('click', e => {
$fileInput.click();
});
$fileInput.addEventListener('change', e => {
console.log('file changed!');
const fileData = $fileInput.files[0];
const reader = new FileReader();
reader.readAsDataURL(fileData);
reader.onloadend = e => {
const $img = document.querySelector('.thumbnail-box img');
$img.src = reader.result;
};
});
</script>
서버에서의 회원가입시 이미지 정보 업로드
- 화면에서 사진이 업로드 된다면 서버에서도 업로드가 되어야 함
MemberController
- 업로드 경로와 회원가입시 서버에 프로필을 업로드하는 코드 추가
- SignUpDto에도
private MultipartFile profileImage;
추가
루트 경로 숨기기 (보안)
- 업로드 경로는 밖으로 드러나지 않고 숨기는 것이 좋다.
application.properties
- application.properties에 업로드 경로를 써놓고
MemberController
- Controller에서 @Value로 값을 받아서 경로를 숨겨서 사용한다.
DB에서 회원가입시 이미지 정보 업로드
- 여러 프로필하고싶으면 새로운 테이블 추가 (1:M)
- 지금은 1대1로 진행하니까 MEMBER테이블에 프로필이미지 컬럼 추가하는 방식으로 진행
Entity
- DB와 1대1 매칭되는 객체에서도 프로필 이미지 업데이트
xml 업데이트
- DB를 업데이트했으므로 xml에서도 save시 프로필 이미지가 업데이트 되도록함
업로드가 완료되면 데이터베이스에 파일의 경로 위치를 저장하기 위해 서버에 업로드 후 업로드 경로를 반환하도록 함
FileUtil - 파일 업로드 수행 후 파일 경로 반환 추가
MemberController
MemberService
- join시 프로필이미지를 받아옴
LocalResourceConfig
- 회원가입시 저장된 프로필 경로를 웹에 쳐보면 사진이 나오는 것을 볼 수 있다.
@Configuration
public class LocalResourceConfig implements WebMvcConfigurer {
@Value("${file.upload.root-path}")
private String rootPath;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/local/**")
.addResourceLocations("file:" + rootPath);
}
}
- 회원가입 시 프로필 이미지를 등록한 회원은 profile_img에 /local ~ 형식으로 파일의 경로를 저장하는 것을 볼 수 있다.
- 또한 LocalResourceConfig의 설정에 의해 profile_img에 저장된 경로를 주소창에 치면 사진이 뜨는 것을 볼 수 있다.