Skip to content

Commit 7dc0909

Browse files
committed
[파일다운로드]
1 parent a4bc7ed commit 7dc0909

File tree

8 files changed

+265
-0
lines changed

8 files changed

+265
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package hello.upload.controller;
2+
3+
import hello.upload.domain.Item;
4+
import hello.upload.domain.ItemRepository;
5+
import hello.upload.domain.UploadFile;
6+
import hello.upload.file.FileStore;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.apache.coyote.Response;
10+
import org.springframework.core.io.Resource;
11+
import org.springframework.core.io.UrlResource;
12+
import org.springframework.http.HttpHeaders;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.stereotype.Controller;
15+
import org.springframework.ui.Model;
16+
import org.springframework.web.bind.annotation.*;
17+
import org.springframework.web.multipart.MultipartFile;
18+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
19+
import org.springframework.web.util.UriUtils;
20+
21+
import java.io.IOException;
22+
import java.net.MalformedURLException;
23+
import java.nio.charset.StandardCharsets;
24+
import java.util.List;
25+
26+
@Slf4j
27+
@Controller
28+
@RequiredArgsConstructor
29+
public class ItemController {
30+
31+
private final ItemRepository itemRepository;
32+
private final FileStore fileStore;
33+
34+
@GetMapping("/items/new")
35+
public String newForm(@ModelAttribute ItemForm form) {
36+
return "item-form";
37+
}
38+
39+
@PostMapping("/items/new")
40+
public String saveForm(@ModelAttribute ItemForm form, RedirectAttributes redirectAttributes) throws IOException {
41+
MultipartFile attachFile = form.getAttachFile();
42+
UploadFile uploadFile = fileStore.storeFile(attachFile);
43+
44+
List<MultipartFile> imageFiles = form.getImageFiles();
45+
List<UploadFile> uploadFiles = fileStore.storeFiles(imageFiles);
46+
47+
//데이터베이스에 저장 (보통 파일의 경로만 저장) (실제 파일은 s3 같은 곳에 저장)
48+
Item item = new Item();
49+
item.setItemName(form.getItemName());
50+
item.setAttachFile(uploadFile);
51+
item.setImageFiles(uploadFiles);
52+
itemRepository.save(item);
53+
54+
redirectAttributes.addAttribute("itemId", item.getId());
55+
56+
return "redirect:/items/{itemId}";
57+
}
58+
59+
@GetMapping("/items/{id}")
60+
public String items(@PathVariable Long id, Model model) {
61+
Item item = itemRepository.findById(id);
62+
model.addAttribute("item", item);
63+
return "item-view";
64+
}
65+
66+
@ResponseBody
67+
@GetMapping("/images/{fileName}")
68+
public Resource downloadImage(@PathVariable String fileName) throws MalformedURLException {
69+
return new UrlResource("file:" + fileStore.getFullPath(fileName)); //해당 리소스에 있는 파일을 가져온다.
70+
}
71+
72+
//첨부파일 다운로드
73+
@GetMapping("/attach/{itemId}")
74+
public ResponseEntity<Resource> downloadAttach(@PathVariable Long itemId) throws MalformedURLException {
75+
Item item = itemRepository.findById(itemId);
76+
String storeFileName = item.getAttachFile().getStoreFileName();
77+
String uploadFileName = item.getAttachFile().getUploadFileName();
78+
79+
UrlResource urlResource = new UrlResource("file:" + fileStore.getFullPath(storeFileName));
80+
81+
log.info("uploadFileName={}", uploadFileName);
82+
83+
String encodedUploadFileName = UriUtils.encode(uploadFileName, StandardCharsets.UTF_8); //파일명 인코딩 => 한글 안깨짐
84+
String contentDisposition = "attachment; filename=\"" + encodedUploadFileName + "\"";
85+
86+
return ResponseEntity.ok()
87+
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) //body에 있는 파일을 다운로드 하라 (contentDisposition 이라는 이름으로)
88+
.body(urlResource);
89+
}
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package hello.upload.controller;
2+
3+
import lombok.Data;
4+
import org.springframework.web.multipart.MultipartFile;
5+
6+
import java.util.List;
7+
8+
@Data
9+
public class ItemForm {
10+
11+
private Long itemId;
12+
private String itemName;
13+
private MultipartFile attachFile;
14+
private List<MultipartFile> imageFiles;
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package hello.upload.domain;
2+
3+
import lombok.Data;
4+
5+
import java.util.List;
6+
7+
@Data
8+
public class Item {
9+
10+
private Long id;
11+
12+
private String itemName;
13+
14+
private UploadFile attachFile;
15+
16+
private List<UploadFile> imageFiles;
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package hello.upload.domain;
2+
3+
import org.springframework.stereotype.Repository;
4+
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
8+
@Repository
9+
public class ItemRepository {
10+
11+
private final Map<Long, Item> store = new HashMap<>();
12+
private Long sequence = 0L;
13+
14+
public Item save(Item item) {
15+
item.setId(++sequence);
16+
store.put(item.getId(), item);
17+
return item;
18+
}
19+
20+
public Item findById(Long id) {
21+
return store.get(id);
22+
}
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package hello.upload.domain;
2+
3+
4+
import lombok.AllArgsConstructor;
5+
import lombok.Data;
6+
7+
@Data
8+
@AllArgsConstructor
9+
public class UploadFile {
10+
11+
private String uploadFileName; //사용자가 업로드 한 이름
12+
13+
private String storeFileName; //서버에서 저장한 이름 (중복 되지 않게 하기 위해)
14+
15+
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package hello.upload.file;
2+
3+
import hello.upload.domain.UploadFile;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.multipart.MultipartFile;
7+
8+
import java.io.File;
9+
import java.io.IOException;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.UUID;
13+
14+
@Component
15+
public class FileStore {
16+
17+
@Value("${file.dir}")
18+
private String fileDir;
19+
20+
public String getFullPath(String fileName) {
21+
return fileDir + fileName;
22+
}
23+
24+
//여러개 저장
25+
public List<UploadFile> storeFiles(List<MultipartFile> multipartFiles) throws IOException {
26+
List<UploadFile> store = new ArrayList<>();
27+
for (MultipartFile multipartFile : multipartFiles) {
28+
if (!multipartFile.isEmpty()) {
29+
UploadFile uploadFile = storeFile(multipartFile);
30+
store.add(uploadFile);
31+
}
32+
}
33+
return store;
34+
}
35+
36+
//단일 저장
37+
public UploadFile storeFile(MultipartFile multipartFile) throws IOException {
38+
if (multipartFile.isEmpty()) {
39+
return null;
40+
}
41+
42+
String originalFilename = multipartFile.getOriginalFilename(); //asdas.png
43+
44+
String storeFile = getStoreFile(originalFilename);
45+
46+
multipartFile.transferTo(new File(getFullPath(storeFile)));
47+
48+
return new UploadFile(originalFilename, storeFile);
49+
50+
}
51+
52+
private String getStoreFile(String originalFileName) {
53+
String ext = extractExt(originalFileName); // png
54+
55+
String uuid = UUID.randomUUID().toString(); // asdas-asfa-asf
56+
57+
return uuid + "." + ext; //ads-ads-ads.png
58+
}
59+
60+
private String extractExt(String originalFileName) {
61+
int i = originalFileName.lastIndexOf(".");
62+
return originalFileName.substring(i + 1);
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
!DOCTYPE HTML>
2+
<html xmlns:th="http://www.thymeleaf.org">
3+
<head>
4+
<meta charset="utf-8">
5+
</head>
6+
<body>
7+
<div class="container">
8+
<div class="py-5 text-center">
9+
<h2>상품 등록</h2>
10+
</div>
11+
<form th:action method="post" enctype="multipart/form-data">
12+
<ul>
13+
<li>상품명 <input type="text" name="itemName"></li>
14+
<li>첨부파일<input type="file" name="attachFile" ></li>
15+
<li>이미지 파일들<input type="file" multiple="multiple"
16+
name="imageFiles" ></li>
17+
</ul>
18+
<input type="submit"/>
19+
</form>
20+
</div> <!-- /container -->
21+
</body>
22+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE HTML>
2+
<html xmlns:th="http://www.thymeleaf.org">
3+
<head>
4+
<meta charset="utf-8">
5+
</head>
6+
<body>
7+
<div class="container">
8+
<div class="py-5 text-center">
9+
<h2>상품 조회</h2>
10+
</div>
11+
상품명: <span th:text="${item.itemName}">상품명</span><br/>
12+
첨부파일: <a th:if="${item.attachFile}" th:href="|/attach/${item.id}|"
13+
th:text="${item.getAttachFile().getUploadFileName()}" /><br/>
14+
<img th:each="imageFile : ${item.imageFiles}" th:src="|/images/${imageFile.getStoreFileName()}|" width="300" height="300"/>
15+
</div> <!-- /container -->
16+
</body>
17+
</html>

0 commit comments

Comments
 (0)