์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- java
- c++
- ์ฌ๊ท
- ์๋ฎฌ๋ ์ด์
- ๋ฐฑํธ๋ํน
- ๊ทธ๋ฆฌ๋
- ๋ฌธ์์ด
- BFS
- error
- dfs
- ๋ฐ์ดํฌ์คํธ๋ผ
- CVE
- ์ฐ์ ์์ ํ
- ์ต๋จ ๊ฒฝ๋ก
- Spring
- web
- GCP
- ์ด๋ถ ํ์
- JPA
- DP
- ์์ ์ ๋ ฌ
- ์คํ
- ๋ถํ ์ ๋ณต
- ๊ตฌํ
- thymeleaf
- Reversing
- ๋งต
- ๋์ ํฉ
- OS
- dynamic debugging
- Today
- Total
hades
[Spring] ํ์ผ ์ ๋ก๋ ๋ณธ๋ฌธ
ํ๋ก์ ํธ๋ฅผ ์งํํ๋ ๋์ค, ํ์ผ ์ ๋ก๋ ๊ธฐ๋ฅ์ด ํ์ํ์ฌ ํ์ต ํ, ํ๋ก์ ํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ๋ฆฌํ ๊ธ์ด๋ค. ๋ณธ์ธ์ ๊ฒ์๊ธ์ ์ด๋ฏธ์ง ํ์ผ์ ์ฌ๋ฆฌ๋ ๊ฒ์ ๋ชฉํ๋ก ํ์๊ณ , ๊ทธ๋ก ์ธํด ๊ฒ์๊ธ๊ณผ ํ์ผ ๊ฐ์ ์ฐ๊ด๊ด๊ณ๊ฐ ์๋ค.
Multipart
๋ฌธ์, ์ซ์ ๋ฐ์ดํฐ๋ ๊ธฐ๋ณธ์ ์ผ๋ก application/x-www-form-urlencoded ๋ฐฉ์์ ์ด์ฉํ๋ค. ํ์ง๋ง, ํ์ผ์ ์ด๋ค๊ณผ ๋ค๋ฅด๊ฒ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๋ก ๋ณด๋ด์ผ ํ๋ค. ๋ฌธ์, ์ซ์, ํ์ผ์ ๋์์ ๋ณด๋ด์ผ ํ๋ ๊ฒฝ์ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ์ด๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด Form ํ๊ทธ์ enctype="multipart/form-data" ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด๋ค.
Content-Disposition์ ์ํด ๊ตฌ๋ถ๋๊ณ , ํ์ผ์ ์ถ๊ฐ์ ์ผ๋ก filename๊ณผ Content-Type์ด ์ถ๊ฐ๋๊ณ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๊ฐ ์ ์ก๋๋ค.
์คํ๋ง์์๋ MultipartFile์ ์ด์ฉํ์ฌ Request๋ก ์ ์ก๋ ํ์ผ์ ์ ๋ฌ๋ฐ์ ์ ์๋ค. MultipartFile์ ์๋ ๋ ๊ฐ์ง ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
- file.getOriginalFileName() : ์ ๋ก๋ ํ์ผ๋ช
- file.transferTo(PATH) : PATH์ ํ์ผ ์ ์ฅ
์ ๋ก๋ ํ์ผ ์ ๋ณด
UploadFile
@Entity
@Getter
@Setter
public class UploadFile {
@Id
@GeneratedValue
@Column(name = "FILE_ID")
private Long id;
private String uploadFileName;
private String storeFileName;
protected UploadFile() {
}
public UploadFile(String uploadFileName, String storeFileName) {
this.uploadFileName = uploadFileName;
this.storeFileName = storeFileName;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "POST_ID")
private Post post;
}
uploadFileName์ ์ฌ์ฉ์๊ฐ ํ์ผ์ ์ ๋ก๋ํ์ ๋, ์ค์ ํ์ผ๋ช ์ด๋ค.
storeFileName์ ์๋ฒ ์ปดํจํฐ์ ์ ์ฅ๋๋ ํ์ผ๋ช ์ด๋ค. ์ฌ๋ฌ ์ฌ์ฉ์๊ฐ ์๋ก ๋ค๋ฅธ ํ์ผ์ ๊ฐ์ ์ด๋ฆ์ผ๋ก ์ ์ฅํ ๊ฒฝ์ฐ, ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
ํ์ผ ์ ์ฅ ๊ด๋ จ
UploadFileService
@Service
@RequiredArgsConstructor
@Transactional
public class UploadFileService {
private final UploadFileRepository uploadFileRepository;
@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);
multipartFile.transferTo(new File(getFullPath(storeFilename)));
UploadFile uploadFile = new UploadFile(originalFilename, storeFilename);
uploadFileRepository.save(uploadFile);
return uploadFile;
}
public List<UploadFile> storeFiles(List<MultipartFile> multipartFiles) throws IOException {
List<UploadFile> storeFileResult = new ArrayList<>();
for (MultipartFile multipartFile: multipartFiles){
if (!multipartFile.isEmpty()){
storeFileResult.add(storeFile(multipartFile));
}
}
return storeFileResult;
}
public void removeFile(Long id){
UploadFile uploadFile = uploadFileRepository.findOne(id);
uploadFileRepository.remove(uploadFile);
}
private String createStoreFilename(String originalFilename){
String ext = extractExt(originalFilename);
String uuid = UUID.randomUUID().toString();
return uuid + "." + ext;
}
private String extractExt(String originalFilename){
int pos = originalFilename.lastIndexOf(".");
return originalFilename.substring(pos+1);
}
}
@Value("${file.dir}")๋ ํ๊ฒฝ ๋ณ์์์ ๊ฐ์ ๊ฐ์ ธ์จ๋ค. file.dir์๋ ํ์ผ๋ช ์ ์ ์ธํ ์๋ฒ ์ปดํจํฐ์ ์ ์ฅ๋ ์์น๊ฐ ์ ์ฅ๋์ด ์๋ค.
getFullPath๋ ํ์ผ์ด ์๋ฒ ์ปดํจํฐ์ ์ ์ฅ๋ ์ต์ข ์์น๋ฅผ ๋ฐํํ๋ค.
storeFile์์๋ multipartFile์ ์ ๋ฌ๋ฐ์ ์๋ฒ ์ปดํจํฐ์ ์ ์ฅํ๊ณ , ์๋ ํ์ผ๋ช ๊ณผ ์๋ฒ์ ์ ์ฅํ ๊ฒฝ๋ก๋ฅผ UploadFile์ ์ ์ฅํ์ฌ DB์ ์ ์ฅํ๊ณ , ๋ฐํํ๋ค.
storeFiles์์๋ multipartFiles๋ฅผ ๋ฐ์ storeFileํ๋ค.
removeFile์์๋ ์ ๋ฌ๋ฐ์ id๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ ๊ฑฐํ๋ค.
createStoreFilename์ ํ์ผ์ด ์๋ฒ ์ปดํจํฐ์์ ์ ์ฅ๋ ๋, ์ด๋ฆ์ด ์ค๋ณต๋์ง ์๋๋ก UUID๋ฅผ ์ด์ฉํ์ฌ ์๋ก์ด ํ์ผ๋ช ์ ์์ฑํ๋ค.
extractExt๋ ํ์ผ ํ์ฅ์๋ฅผ ๋ฐํํ๋ค.
createPost.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>์๋ฆฌ ์ธ์ฆ ๊ธ ์์ฑ</title>
</head>
<body>
<h1>์๋ฆฌ ์ธ์ฆ ๊ธ ์์ฑ</h1>
<h2>๋ด๊ฐ ๋ง๋ ์๋ฆฌ ์๋ํ๊ธฐ</h2>
<form th:action="|/post/create/${foodId}|" method="post" enctype="multipart/form-data" th:object="${postDto}">
<ul>
<li>์ด๋ฏธ์ง <input type="file" th:field="*{imageFiles}" multiple="multiple" required></li>
<li>ํ๋ง๋ <input type="text" th:field="*{comment}" required></li>
</ul>
<button type="submit">๋ฑ๋ก</button>
</form>
</body>
</html>
PostController
@PostMapping("/post/create/{foodId}")
public String writePost(@PathVariable Long foodId, @ModelAttribute PostDto postDto, HttpServletRequest request) throws IOException {
List<UploadFile> imageFiles = uploadFileService.storeFiles(postDto.getImageFiles());
String comment = postDto.getComment();
HttpSession session = request.getSession();
Member loginMember = (Member)session.getAttribute("LOGIN_MEMBER");
Food food = foodRepository.findOne(foodId);
Post post = Post.createPost(loginMember, food, imageFiles, comment);
postService.write(post);
return "redirect:/food/foodInfo/" + foodId;
}
@ResponseBody
@GetMapping("/post/images/{filename}")
public UrlResource downloadImage(@PathVariable String filename) throws MalformedURLException {
return new UrlResource("file:" + uploadFileService.getFullPath(filename));
}
writePost๋ ์ ๋ฌ๋ฐ์ imageFiles๋ฅผ ์ ์ฅํ๊ณ , UploadFile ๋ฆฌ์คํธ๋ก ๋ฐํ๋ฐ์, Post์ ์ฐ๊ฒฐ์ํจ๋ค.
downloadImage๋ img ํ๊ทธ์์ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ํด ํ์ํ UrlResource๋ฅผ ์์ฑํ๋ค. UrlResource์์ "file:"์ ๊ผญ ๋ถ์ฌ์ผ๋๋ ํ๋๋ฐ, ๊ณต์๋ฌธ์๋ฅผ ์ฐธ์กฐํด๋ณด๋ ๋ถ์ฌ์ผ ํ๋ค.
UrlResource (Spring Framework 6.1.11 API)
This delegate creates a java.net.URL, applying the given path relative to the path of the underlying URL of this resource descriptor.
docs.spring.io
'๐๐ปโโ๏ธ ๊ธฐ๋ณธํ๋ จ > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] ๊ฒฝ๋ก ์ค์ ์ ๋์๋ฌธ์ ๊ตฌ๋ถ์ ์ค์์ฑ (0) | 2024.08.19 |
---|---|
[Spring] ์คํ๋ง AI (0) | 2024.08.14 |
[Spring] redirect ๊ฒฝ๋ก ์ค์ ์ ๋ฌธ์ ์ฌ์ฉ ์ฃผ์ (0) | 2024.08.12 |
[Thymeleaf] ํ ์คํธ ๋ฆฌํฐ๋ด (0) | 2024.08.05 |
[Thymeleaf] ํ ํ๋ฆฟ ์์น (0) | 2024.07.29 |