사진 업로드 샘플링 3 (+UUID 롤링)

coding S's avatar
Apr 19, 2024
사진 업로드 샘플링 3 (+UUID 롤링)

[ Pic 테이블 생성 ]

package com.mtcoding.fileapp.pic; import jakarta.persistence.*; import lombok.Data; @Data @Table(name = "pic_tb") @Entity public class Pic { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String title; private String imgFilename; //파일 이름(파일 경로) }
💡
파일 자체는 하드디스크에 저장되고, 파일의 경로만 저장!
 

[ PicRepository ]

package com.example.fileapp.pic; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Repository public class PicRepository { private final EntityManager em; @Transactional public void insert(String title, String imgFilename){ Query query = em.createNativeQuery("insert into pic_tb(title, img_filename) values(?,?)"); query.setParameter(1, title); query.setParameter(2, imgFilename); query.executeUpdate(); } public Pic findById(int id){ Query query = em.createNativeQuery("select * from pic_tb where id = ?", Pic.class); query.setParameter(1, id); return (Pic) query.getSingleResult(); } }
💡
title과 imgFilename을 DB에 저장
 

[ UUID - 롤링 (32자의 랜덤 수) ]

💡
롤링이란? 데이터나 숫자가 주기적이거나 연속적으로 변화하는 과정
notion image
package com.mtcoding.fileapp.util; import org.junit.jupiter.api.Test; import java.util.UUID; public class UUIDTest { @Test public void rolling_test() { UUID uuid = UUID.randomUUID(); //랜덤 해시값 리턴 String value = uuid.toString(); System.out.println(value); } }
notion image
UUID의 해쉬값은 충돌날 확률이 아주아주아주극그극ㄱ극극 낮다. (16의 32승) 이런게 파일의 해시값으로 들어가면 어떻게 될까? -> 파일명을 계속 바꿔치기 함! HELLO.PNG 이라는 이름의 파일이 수만개가 동일하게 들어와도 앞에 해시값이 다 다를 것. -> 다 다른 파일로 인식(?)하여 충돌날 확률이 X
 

[ PicController ]

package com.example.fileapp.pic; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.UUID; @RequiredArgsConstructor @Controller public class PicController { private final PicRepository picRepository; @PostMapping("/upload") public String upload(PicRequest.UploadDTO requestDTO){ // 1. 데이터 전달 받고 String title = requestDTO.getTitle(); MultipartFile imgFile = requestDTO.getImgFile(); // 2. 파일저장 위치 설정해서 파일을 저장 //리팩토링 String imgFilename = UUID.randomUUID()+"_"+imgFile.getOriginalFilename(); Path imgPath = Paths.get("./src/main/resources/static/upload/"+imgFilename); try { Files.write(imgPath, imgFile.getBytes()); // 3. DB에 저장 (title, realFileName) picRepository.insert(title, imgFilename); } catch (IOException e) { throw new RuntimeException(e); } return "redirect:/"; } @GetMapping("/") public String index(){ return "index"; } @GetMapping("/uploadForm") public String uploadForm(){ return "uploadForm"; } @GetMapping("/uploadCheck") public String uploadCheck(){ return "uploadCheck"; } }
💡
롤링해서 저장을 하고, 롤링한 값이 db에 들어갈 것이다!
* 생성된 UUID 문자열과 원본 파일 이름 사이에 언더스코어(_)를 추가하여, 두 문자열을 구분 * imgFile.getOriginalFilename() 파일 업로드를 처리할 때 파일의 원본 이름을 가져오는 메서드 imgFile은 업로드된 파일을 나타내는 객체이며, getOriginalFilename() 메서드는 업로드된 파일의 원래 이름을 문자열로 반환. ex) 사용자가 photo.jpg라는 이름의 이미지 파일을 업로드했다면, 이 메서드는 "photo.jpg"라는 문자열을 반환 즉, 업로드된 파일에 대해 고유한 이름을 생성. (생성된 파일 이름은 UUID_원본파일이름 형태) f47ac10b-58cc-4372-a567-0e02b2c3d479_photo.jpg 식으로 저장된다. 이 방식은 파일 이름 충돌을 방지하고, 서버에 파일을 저장할 때 각 파일에 대해 유일한 식별자를 제공하는 효과적인 방법!
 

 
notion image
💡
DB에 저장되는 건 realFileName 나중에 이런 건 메소드를 따로 빼서 재사용 하는게 좋다
 

[ 근데 DB에 들어오지 않는다 ]

notion image
💡
static에 넣지말고 외부 upload 폴더를 생성하고, 여기에다가 넣어주자 → Path imgPath = Paths.get("./upload/" + realFileName); 로 변경!

[ PicController ]

@PostMapping("/upload") public String upload(PicRequest.UploadDTO requestDTO) { System.out.println(requestDTO.getTitle()); String title = requestDTO.getTitle(); MultipartFile imgFile = requestDTO.getImgFile(); String imgFilename = UUID.randomUUID() + "_" + imgFile.getOriginalFilename(); Path imgPath = Paths.get("./upload/"+imgFilename); try { Files.write(imgPath, imgFile.getBytes()); picRepository.insert(title, imgFilename); } catch (IOException e) { throw new RuntimeException(e); } return "redirect:/"; }
 

static이 아닌 다른 외부 폴더 개방 → WebMvcConfig 로 설정을 해줘야 함

notion image
package com.example.fileapp.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.resource.PathResourceResolver; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { WebMvcConfigurer.super.addResourceHandlers(registry); registry .addResourceHandler("/upload/**") .addResourceLocations("file:./upload/") .setCachePeriod(60 * 60) // 초 단위 => 한시간 .resourceChain(true) .addResolver(new PathResourceResolver()); } }
.addResourceHandelr(/upload/**), upload/** 같은 패턴이 들어오면 모두 아래의 ./upload 폴더에 들어가게끔 설정 (/upload로 시작하는 모든 요청이 이 핸들러에 의해 처리) .addResourceLocations("file:./upload") 웹에 이 경로를 열어줄게 하고 설정하는 것. 여기서 정적 리소스를 찾는다. (이 핸들러가 처리하는 요청에 대한 리소스가 저장된 위치를 지정) .setCachePeriod(60 * 60) 브라우저 캐싱을 위한 시간을 설정. 여기서는 60초(1분) * 60 = 3600초, 즉 1시간 동안 캐싱하도록 설정 .resourceChain(true) 리소스 체인 활성화. 리소스의 위치를 찾고 내용을 최적화하는데 사용 .addResolver(new PathResourceResolver()): PathResourceResolver 요청된 리소스의 경로를 해석하는 리졸버. 요청된 리소스의 실제 경로를 찾을 수 있다.
💡
/upload로 시작하는 모든 요청을 프로젝트 루트의 upload 폴더에 있는 정적 리소스로 매핑하고, 해당 리소스들을 1시간 동안 캐싱하며, 리소스 요청 경로를 해석하기 위해 PathResourceResolver를 사용하도록 설정
 

[ uploadCheck에도 경로 설정 ]

notion image
만약 c드라이브라면 file:c:\\upload 이런 식으로... 외부 폴더 설정...

만약

notion image
이렇게 되면 static에 있는 a 폴더에 저장한다
 

저장 됐을거다 그럼 이제

[ uploadCheck.mustache ]

<h1>제목 : {{pic.title}}</h1> <img src="/upload/{{pic.imgFilename}}" width="500" height="500" alt="사진없음">

[ PicController ]

@GetMapping("/uploadCheck") public String uploadCheck(HttpServletRequest request){ Pic pic = picRepository.findById(1); request.setAttribute("pic", pic); return "uploadCheck"; }
💡
설정해주자! 그럼 끝!
 

[ static 에서 외부 폴더로 옮긴 이유 ]

h2에서 리로드가 한번 더 되서 초기화가 되서 그렇습니다 DB에서 안나와서 MVCconfig를 썼던거는 DB에 안나와서
💡
웹에 개방된 폴더가 static과 upload폴더가 되었다.
 

 
💡
multipart/form-data 로 사진 받고, WebMvcConfig으로 사진이 저장되는 경로를 바꿀 수 있다. …정도만 알아두자!
 

[ 화면 / DB 확인 ]

notion image
notion image
notion image
notion image
Share article

codingb