[스프링 부트] 35. 파일 업로드 구현 - 웹 게시판 v6

KangHo Lee's avatar
Dec 05, 2024
[스프링 부트] 35. 파일 업로드 구현  - 웹 게시판 v6

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

devleekangho