π‘ κ²μν - νμΌ μ λ‘λ 2μμ κ°μ ν μ μΌλ‘
λ₯Ό μ΄μΌκΈ° νλ€.
μ΄λ² κΈμμλ μμ±ν νμΌ μ λ‘λμ μ½λλ₯Ό κ°μ νκ³ μ΄λ―Έμ§λ₯Ό ν΄λ¦ν΄μ 보μ΄λ κ²μ΄ μλ κ²μκΈ μμΈνμ΄μ§μμ 보μ΄λλ‘ κ°μ νλ€.
BoardServiceμ save λ©μλ
public void save(Board board, MultipartFile file) throws Exception {
if(!file.isEmpty()){ //νμΌ μ
λ‘λκ° μλ κ²½μ°μλ§ μ€ν
UploadFile uploadFile = fileStore.storeFile(file);
board.setFilename(uploadFile.getUploadFilName());
board.setFilepath(uploadFile.getStoreFileName());
}
boardRepository.save(board);
}
FileStore
@Component
public class FileStore {
@Value("${file.dir}")
private String fileDir;
public String getFullPath(String filename) {
return fileDir + filename;
}
public UploadFile storeFile(MultipartFile multipartFile) throws IOException {
if (multipartFile.isEmpty()) {
return null;
}
String originalFilename = multipartFile.getOriginalFilename();
String storeFileName = createStoreFileName(originalFilename); //λλ€μ uuidλ₯Ό μΆκ°ν νμΌ μ΄λ¦
multipartFile.transferTo(new File(getFullPath(storeFileName)));
return new UploadFile(originalFilename, storeFileName);
}
// μλ² λ΄λΆμμ κ΄λ¦¬νλ νμΌλͺ
μ μ μΌν μ΄λ¦μ μμ±νλ UUIDλ₯Ό μ¬μ©ν΄μ μΆ©λνμ§ μλλ‘ νλ€.
private String createStoreFileName(String originalFilename) {
String ext = extractExt(originalFilename);
String uuid = UUID.randomUUID().toString(); //νμΌ μ΄λ¦ μ€λ³΅ λ°©μ§
return uuid + "." + ext;
}
//νμ₯μλ₯Ό λ³λλ‘ μΆμΆν΄μ μλ² λ΄λΆμμ κ΄λ¦¬νλ νμΌλͺ
μλ λΆμ¬μ€λ€.
//Ex) a.pngλΌλ μ΄λ¦μΌλ‘ μ
λ‘λνλ©΄ 2def12-42qd-3214-e2dqda2.png μ κ°μ΄ νμ₯μλ₯Ό μΆκ°ν΄μ μ μ₯νλ€.
private String extractExt(String originalFilename) {
int pos = originalFilename.lastIndexOf("."); //νμΌμ νμ₯μ μΆμΆ ex) .png .img
return originalFilename.substring(pos + 1);
}
}
μλ BoardServiceμ save λ©μλμμ μ²λ¦¬νλ νμΌ μ
λ‘λ κ³Όμ μ FileStore
ν΄λμ€λ₯Ό λ§λ€μ΄μ κ°μ μ νλ€. λλΆμ save λ©μλκ° κΉλν΄μ‘λ€.
μ΄μ κ²μκΈ μμΈ νμ΄μ§μμ μ λ‘λ ν μ΄λ―Έμ§κ° 보μ΄λλ‘ μ²λ¦¬λ₯Ό νκ² λ€.
μ΄λ―Έμ§ νμΌ λΆλΆ
<span th:if="${post.filepath != null}">
<img th:src="@{'/images/' + ${post.filepath}}" width="300" height="300"/><br>
</span>
λ°©λ²μ κ°λ¨νλ€. κ²μκΈ μμΈ νμ΄μ§λ₯Ό 보μ¬μ£Όλ htmlμ μμ μ½λλ₯Ό μΆκ°νλ©΄ λλ€. post.filepath
λ₯Ό ν΄μ νμΌμ κ²½λ‘λ₯Ό λΆλ¬μμ μ΄λ―Έμ§λ₯Ό 보μ¬μ€λ€.
μ μ©μ΄ λλμ§ νμΈν΄λ³΄μ.
μ¬μ§μ²λΌ μμ±μ νκ³ μ΄λ―Έμ§ νμΌμ 첨λΆνκ² λ€.
κ²μκΈ μμ± ν κ²μκΈ μμΈ νμ΄μ§μ λ€μ΄κ°λ μ΄λ―Έμ§κ° 보μ΄μ§ μλλ€.
λν, κ΄λ¦¬μ λꡬλ₯Ό 보면 μ΄λ―Έμ§ νμΌμ΄ HTTP Status 404 μ€λ₯κ° λ κ²μ΄ 보μΈλ€.
μ΄μ κ° λκΉ?
μ΄λ―Έμ§ νμΌμ λλ¬μ λ€μ΄κ° νμ΄μ§μ΄λ€. μΉ λΈλΌμ°μ λ νμΌμ κ²½λ‘λ§ λ§ν¬ν΄μ£Όλ κ²μ΄μ§ DBκ° νμΌμ κ°μ§κ³ μλ κ²μ΄ μλλ€.
λλ¬Έμ Files/μ΄λ―Έμ§ κ²½λ‘
μ λν 컨νΈλ‘€λ¬λ₯Ό λ§λ€μ΄μ€μΌνλ€.
BoardControllerμ μΆκ°λ λ©μλ
@ResponseBody
@GetMapping("/images/{filename}") // images/DBμ μ μ₯λ νμΌ κ²½λ‘
public Resource downloadImage(@PathVariable String filename) throws MalformedURLException {
// "file:/Users/../0d713e88-4723-4088-bb4b-be039b6f9b47.png"
//κ²½λ‘μ μλ νμΌμ μ κ·Όν΄μ νμΌμ μ€νΈλ¦ΌμΌλ‘ λ°νμ ν¨
return new UrlResource("file:" + fileStore.getFullPath(filename));
}
μ΄μ μ μλ¬ μ¬μ§μ 보면 κ²½λ‘κ° /images/DBμ μ μ₯λ νμΌ κ²½λ‘
λ‘ λμ΄μ¨λ€. @GetMapping
μΌλ‘ κ²½λ‘λ₯Ό λ°λλ€.
Resource
Springμ Resource μΈν°νμ΄μ€λ Resourceμ λν μ κ·Όμ μΆμννκΈ° μν μΈν°νμ΄μ€μ΄λ€.
file: http: μ΄λ°μμ Prefixλ‘ νλ‘ν μ½μ λͺ μν΄μ£Όκ³ ν΄λΉ 리μμ€μ μμΉλ₯Ό μλ €μ£Όλ URLλ°©μμ ν΅ν΄μ 리μμ€μ μμΉλ₯Ό μλ €μ£Όλ λ°©μ
Resource resource = new UrlResource("file:{μ λκ²½λ‘}");
downloadImage λ©μλλ Springμ Resource μΈν°νμ΄μ€λ₯Ό μ¬μ©ν΄μ UrlResource
λ‘ μ΄λ―Έμ§ νμΌμ μ½μ΄μ @Responsebodyλ‘ μ΄λ―Έμ§ λ°μ΄λ리λ₯Ό λ°ννλ€.
μ΄μ λ€μ κ²μκΈμ λ±λ‘νκ³ μ΄λ―Έμ§ νμΌμ νμΈν΄λ³΄μ.
μ΄μ κ³Ό λμΌνκ² κ²μκΈμ μμ±ν΄μ€λ€.
κ²μκΈ μμ± ν κ²μκΈ μμΈ νμ΄μ§λ₯Ό 보λ μ
λ‘λν μ΄λ―Έμ§ νμΌμ΄ μ μμ μΌλ‘ 보μΈλ€. λν κ΄λ¦¬μ λꡬλ₯Ό 보면 HTTP statusκ° 400μ΄ μλ 200μΌλ‘ μ€λ κ²μ΄ 보μΈλ€.
μ¬μ ν λΆμ‘±ν μ μ΄ λ§λ€.
νμΌ λ€μ΄λ‘λ κΈ°λ₯
μ λ‘λν νμΌμ μμ νκ² λ§λ€κ³ μΆλ€.
첨λΆνμΌ μ λ‘λ, λ€μ΄λ‘λ
λ±λ± νμΌ μ λ‘λ λΆλΆμ μ μΈνκ³ λ€λ₯Έ λΆλΆμμλ μμ ν΄μΌ ν λΆλΆμ΄ λ§λ€.
νμ λΆμ‘±ν¨μ λλΌμ§λ§ μ‘°κΈμ© μ±μ₯νλ μμ μ 보λ λΏλ―ν¨λ κ°μ΄ λλλ€. μ¬μ ν κ°λ°μ΄ μ¬λ°λ€. μ§κΈκΉμ§μ μΈμμμ νλ κ² μ€ λλ²μ§Έλ‘ μ¬λ°λ€.
첫λ²μ§Έλ μ¬μμΉκ΅¬λ λ Έλκ±°..μμΌλ‘λ λ°μ νλ λ΄κ° λκΈΈ λ°λΌλ©° μ€λμ κΈμ μ¬κΈ°μ λλ΄κ² λ€!
κΈμμ λΆμ‘±ν λΆλΆμ΄ λ§μ κ² κ°μ΅λλ€. λͺ¨λ μ§μ μ νμν©λλ€!!
λ μμΈν μ½λλ κΉνλΈλ₯Ό μ°Έκ³ ν΄μ£ΌμΈμ!
κΉνλΈ: https://github.com/pp8817/ToyProjectBoard