
[ 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자의 랜덤 수) ]
롤링이란?
데이터나 숫자가 주기적이거나 연속적으로 변화하는 과정

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);
}
}

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 식으로 저장된다.
이 방식은 파일 이름 충돌을 방지하고,
서버에 파일을 저장할 때 각 파일에 대해 유일한 식별자를 제공하는 효과적인 방법!

DB에 저장되는 건 realFileName
나중에 이런 건 메소드를 따로 빼서 재사용 하는게 좋다
[ 근데 DB에 들어오지 않는다 ]

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 로 설정을 해줘야 함

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에도 경로 설정 ]

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

이렇게 되면 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 확인 ]




Share article