각진 세상에 둥근 춤을 추자
[CRUD] 5. 게시글 작성 기능 구현 - 단일 파일 업로드 및 다운로드 본문
<input type="file"> 태그를 이용해 단일 파일 업로드 기능을 구현한다.
1. application.properties
application.properties에 파일 업로드를 위한 multipart 설정을 추가한다.
#############################################
#파일 업로드 설정
#############################################
spring.servlet.multipart.location=img/
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB
2. HTML 화면 작성하기
HTML에 파일 업로드를 위한 <input> 태그를 작성한다.
아이디를 fileUpload라고 작성한다.
<input type="file" id="fileUpload">
3. 파일 정보를 저장할 VO 작성하기
파일 정보를 저장할 VO를 작성한다.
@Data
public class FileVO {
private int fno; // 파일 번호
private String parent; // 게시글 번호
private String newName; // 새 파일명
private String oriName; // 기존 파일명
private String del_yn; // 삭제여부
/* 추가: 게시글 번호 */
private String no;
}
4. JS - Ajax로 파일 전송하기
let btnSave = document.getElementById("btnSave"); // 저장 버튼
/* Initialize */
document.addEventListener('DOMContentLoaded', function() {
/** CRUD 버튼 이벤트 등록 */
addEventListenerCRUDBtn();
});
function addEventListenerCRUDBtn(){
btnSave.addEventListener("click", fnSave);
}
function fnSave() {
let fileUpload = document.getElementById("fileUpload"); // 파일 업로드
let formData = new FormData(); // 파일 업로드를 위한 폼 생성
let selectedFile = fileUpload.files[0]; // 업로드한 파일
let jsonData = {};
jsonData = {
"name":selectedFile.name
};
if(confirm("등록하시겠습니까?")){
ajaxAPI("/board/write", jsonData, "POST").then(response => {
// 글 번호
let no = response.no;
// 파일 업로드
console.log("selectedFile: " + selectedFile);
formData.append("file", selectedFile);
formData.append("oriName", selectedFile.name);
$.ajax({
url: '/board/upload',
processData: false,
contentType: false,
data: formData,
type: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader(header, token);
},
success: function(result) {
console.log("result: " + result);
}
})
}
}
5. Controller - 서버에 업로드하기
Controller에 파일을 업로드할 경로(서버)를 설정한 후 해당 경로에 업로드한다.
@PostMapping("/board/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file){
String uploadPath = "/Users/yiwonjeong/Desktop/upload"; // 파일 업로드 경로
String originalFilename = file.getOriginalFilename(); // 파일 기존 이름
log.warn("multipart upload: "+ file);
log.warn("Original Filename: " + file.getOriginalFilename());
log.warn("Content Type: " + file.getContentType());
log.warn("Size: " + file.getSize());
// 파일 업로드 경로에 파일 업로드하기
Path filePath = Paths.get(uploadPath, originalFilename);
try {
Files.write(filePath, file.getBytes());
return ResponseEntity.ok("File uploaded successfully!");
} catch (IOException e) {
log.error("Error uploading file: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to upload file. Please try again.");
}
}
6. Service - 파일 정보를 DB에 저장하기
Service에서 파일명을 새로 수정한 후 해당 파일의 정보를 DB에 저장한다.
public void updateBoard(BoardVO boardVO){
// 새 파일명 생성
String oriName = boardVO.getName();
String tempName = oriName.substring(oriName.lastIndexOf("."));
String newName = UUID.randomUUID().toString() + tempName;
int fno = boardVO.getFno();
// 파일 정보 작성
FileVO fileVO = new FileVO();
fileVO.setOriName(oriName);
fileVO.setNewName(newName);
fileVO.setParent(boardVO.getNo());
fileVO.setFno(fno);
dao.updateFile(fileVO);
}
7. Dao - xml 파일과 연결하기
public void updateFile(FileVO fileVO);
8. xml - DB에 저장하기
<insert id="insertFile" parameterType="kr.co.crud.domain.FileVO">
INSERT INTO CRUD_FILE SET
`parent` = #{parent},
`newName` = #{newName},
`oriName` = #{oriName}
</insert>
9. 파일 다운로드
앞서 업로드한 파일을 화면에 표시한 뒤, 클릭 시 파일을 다운로드한다.
아래 HTML 코드와 같이 파일명과 파일번호 값을 넣은 후 아이디를 작성한다.
<div class="view-file" id="fileDownload" th:text="${board.oriName}" th:data-fno="${board.fno}">file</div>
해당 페이지와 연결된 JS에서 클릭 시 파일을 다운로드 받을 수 있게 한다.
let btnFileDownload = document.getElementById("fileDownload"); // 파일 다운로드 버튼
/* Initialize */
document.addEventListener('DOMContentLoaded', function() {
/** CRUD 버튼 이벤트 등록 */
addEventListenerCRUDBtn();
});
function addEventListenerCRUDBtn(){
btnFileDownload.addEventListener("click", fnFileDownload);
}
/* 파일 다운로드 */
function fnFileDownload() {
let fileNo = btnFileDownload.getAttribute("data-fno");
ajaxAPI("/board/fileDownload?fno=" + fileNo, null, "GET").then(response => {
console.log("fileDownload ajax success");
});
}
해당 파일의 번호를 통해 서버에 업로드된 파일을 다운로드 받는다.
아래는 컨트롤러 파일 코드이다.
편의상 selectFile을 통해 파일 번호로 파일 이름을 찾아주었다.
@GetMapping("/board/fileDownload")
@ResponseBody
public ResponseEntity<Resource> fileDownload(@RequestParam("fno") String fno) throws IOException {
int fnoInt = Integer.parseInt(fno);
FileVO file = service.selectFile(fnoInt);
ResponseEntity<Resource> response = service.fileDownload(file.getOriName());
return response;
}
서비스 파일에서 정해둔 서버(경로)에 저장된 파일을 찾아 다운로드한다.
public ResponseEntity<Resource> fileDownload(String oriName) throws IOException {
String uploadPath = "/Users/yiwonjeong/Desktop/upload/";
Path path = Paths.get(uploadPath, oriName);
String contentType = Files.probeContentType(path);
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(ContentDisposition.builder("attachment")
.filename(oriName)
.build());
headers.add(HttpHeaders.CONTENT_TYPE, contentType);
Resource resource = new InputStreamResource(Files.newInputStream(path));
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}
'프로젝트 > CRUD' 카테고리의 다른 글
[CRUD] 4. 게시글 작성 기능 구현 - 네이버 스마트에디터 (0) | 2024.01.10 |
---|---|
[CRUD] 3. 로그인 기능 구현 - 스프링 시큐리티 (1) | 2024.01.10 |
[CRUD] 2. 회원가입 기능 구현 - Daum 우편번호 API (0) | 2024.01.10 |
[CRUD] 1. 페이지 화면 구현 (0) | 2023.12.30 |