1. Upload 엔티티
- upload한 파일을 Upload 객체에 넣을 예정입니다.
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "upload_tb")
@Entity
public class Upload {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String profileUrl;
@Builder
public Upload(Integer id, String username, String profileUrl) {
this.id = id;
this.username = username;
this.profileUrl = profileUrl;
}
}
- 속성으로 올린 사람의 이름(username)과 올린 파일의 Url(profileUrl)을 가지고 있습니다.
2. UploadRepository
@RequiredArgsConstructor
@Repository
public class UploadRepository {
private final EntityManager em;
public Upload findById(Integer id) {
return em.find(Upload.class, id);
}
public void save(Upload upload) {
em.persist(upload);
}
}
- id로 검색 + 파일 저장
3. UploadService
@RequiredArgsConstructor
@Service
public class UploadService {
private final UploadRepository uploadRepository;
@Transactional
public void v1사진저장(UploadRequest.V1DTO v1DTO) {
// 1. DTO에 사진파일명을 롤링 시킨다.
String imgName = UUID.randomUUID() + "_" + v1DTO.getImg().getOriginalFilename();
String profileUrl = "images/" + imgName;
// db에 저장할 Url
String dbUrl = "/upload/" + imgName;
// 2. DTO에 사진을 파일로 저장 (images 폴더)
try {
Path path = Paths.get(profileUrl);
// 파일을 프로젝트 폴더(upload)안의 images 폴더에 저장
Files.write(path, v1DTO.getImg().getBytes());
// 외부에서 접근 가능한 경로로 db에는 다르게 저장
uploadRepository.save(v1DTO.toEntity(dbUrl));
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
public Upload v1사진보기() {
return uploadRepository.findById(1);
}
}
- UUID.randomUUID()
- 파일 이름 앞에 추가함으로써 같은 이름을 가진 파일과 충돌을 방지합니다.
4. 사용자가 접근 가능한 url 설정
WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
// 1. 절대경로 file:///c:/upload/
// 2. 상대경로 file:./upload/
registry
.addResourceHandler("/upload/**") // html에서 경로를 적으면
.addResourceLocations("file:" + "./images/") // 웹서버의 /images/ 폴더 경로를 찾음
.setCachePeriod(60 * 60); // 초 단위 => 한시간
}
}
- .addResourceHandler("/upload/**")
- 사용자는
http://localhost:8080/upload/파일이름.확장자명
로 db에 저장된 이미지에 접근할 수 있습니다. - 그 외의 경로는 막혀있습니다.
- .addResourceLocations("file:" + "./images/")
- 웹서버에 저장된 이미지 경로를 설정합니다.
- setCachePeriod(60 * 60);
- 정적 리소스의 캐시 유효 기간을 설정합니다.
- 단위는 second 이므로 60 * 60은 1시간을 의미합니다.
5. UploadController
@RequiredArgsConstructor
@Controller
public class UploadController {
private final UploadService uploadService;
// form 태그 이동
@GetMapping("/file1")
public String file1(Model model) {
return "file1";
}
@PostMapping("/v1/upload")
public String v1Upload(UploadRequest.V1DTO v1DTO) {
System.out.println(v1DTO.getUsername());
// 업로드된 파일 이름
System.out.println(v1DTO.getImg().getOriginalFilename());
// 업로드된 파일의 확장자(예시: image/jpeg)
System.out.println(v1DTO.getImg().getContentType());
// 요청, 응답 처리 완료(Controller의 책임을 완수) -> 다른 로직은 서비스로
uploadService.v1사진저장(v1DTO);
return "index";
}
// form 태그 이용
@GetMapping("/file1-check")
public String file1Check(Model model) {
Upload upload = uploadService.v1사진보기();
model.addAttribute("model", upload);
return "file1-check";
}
}
6. 업로드에 사용하는 DTO
UploadRequest.V1DTO
public class UploadRequest {
@Data
public static class V1DTO {
private String username;
private MultipartFile img;
public Upload toEntity(String profileUrl) {
Upload upload = Upload.builder()
.username(this.username)
.profileUrl(profileUrl)
.build();
return upload;
}
}
}
7. 파일 전송 Form
file1.mustache
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<h1>사진 파일 전송</h1>
<hr>
<form action="/v1/upload" method="post" enctype="multipart/form-data">
<input type="text" name="username"> <br>
<input type="file" name="img"> <br>
<button type="submit">전송</button>
</form>
</body>
</html>
- multipart/form-data
- 웹 애플리케이션에서 파일과 데이터를 함께 전송할 때 사용되는 MIME 타입입니다.
8. 업로드 된 이미지 파일을 확인하는 화면
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<img src="{{model.profileUrl}}">
</body>
</html>
Share article