SpringBoot에서 파일 업로드를 위한 종속성을 추가해줘야한다.
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
Front-end에서 Back-end로 요청을 보낼 때, HTTP Request를 통해 요청이 전송된다. 이때 각종 데이터는 HTTP Request의 Body
에 데이터를 저장하여 보내게된다. Body
에 들어가는 데이터의 형태는 Header
에 Content-type을 통하여 어떠한 데이터를 전송하는 지 명시하게 된다.
파일 전송을 하려면, Content-type을 multipart/form-data
로 지정해줘야한다.
<form action="/uploadfiles" method="post" enctype="multipart/form-data">
파일명 : <input type="file" name="myfile">
<button type="submit">제출하기</button>
</form>
React에서 서버와 통신하기 위해서 Axios
를 사용해야하는데, 해당 라이브러리는 따로 작성된 글을 참고한다.
링크
파일 데이터를 비동기를 보내기 위해서는 new FormData()
라는 객체를 생성하여 사용한다.
const formData = new FormData();
생성된 객체에 파일 데이터와 JSON 데이터를 담기 위해 FormData
에서 append()
라는 메소드를 제공한다. JSON 데이터는 content-type을 application/json
로 명시하여 전송하지만, 파일 데이터가 있기 때문에 JSON.stringify()
메소드를 통해 JSON 객체를 텍스트로 변환 후 new Blob()
을 통해 담아준다.
const formData = new FormData();
const contentsData = {
userNo: user.userInfo.no,
title,
content,
categoryCode,
dues,
personNumber
}
const fileData = inputRef.current.file.files;
for(let i = 0; i < fileData.length; i++) {
formData.append("file", fileData[i]);
}
formData.append("contentsData", new Blob([JSON.stringify(contentsData)], { type: "application/json" }));
그리고 Axios로 POST 방식으로 데이터를 전송하면 된다.
파라미터는 HttpServletRequest
와 @RequestPart
를 통해 데이터를 받아줄려고한다. 파일데이터를 받기 위해서는 MultipartHttpServletRequest
라는 클래스가 필요한데, HttpServletRequest
의 하위 클래스다.
@PostMapping("/insert")
public Map<String, Object> insertMeeting(
HttpServletRequest httpServletRequest,
@RequestPart(value = "contentsData") MeetingVO meetingVO {
...
...
}
@Component
public class FileUploadUtil {
private static final Logger logger
= LoggerFactory.getLogger(FileUploadUtil.class);
public List<Map<String, Object>> mulitiFileUpload(HttpServletRequest request,
int uploadFlag) throws IllegalStateException, IOException {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest)request;
List<Map<String, Object>> list = new ArrayList<>();
List<MultipartFile> fileMap = multiRequest.getFiles("file");
for(MultipartFile multipartFile : fileMap) {
if(!multipartFile.isEmpty()) {
long fileSize = multipartFile.getSize(); // 파일 크기
String oName = multipartFile.getOriginalFilename(); // 원래 파일명
//변경된 파일이름 구하기
String fileName = getUniqueFileName(oName);
//업로드할 폴더 구하기
String uploadPath
= getUploadPath(request, uploadFlag);
//파일 업로드 처리
File file = new File(uploadPath, fileName);
multipartFile.transferTo(file);
//업로드된 파일 정보 저장, (파일크기, 파일명2개를 VO나 Map으로 묶으면 된다)
//[1] Map에 저장
Map<String, Object> resultMap = new HashMap<>(); // <>뒤에는 생략가능
resultMap.put("fileName", fileName);
resultMap.put("fileSize", fileSize);
resultMap.put("originalFileName", oName);
//[2] 여러 개의 Map을 List에 저장
list.add(resultMap);
}
}
return list;
}
public String getUniqueFileName(String fileName) {
//파일명이 중복될 경우 파일이름 변경하기
//파일명에 현재시간(년원일 시분초 밀리초)을 붙여서 변경된 파일이름 구하기
//ex) a.txt => a_20220602113820123.txt
//순수 파일명만 구하기 => a
int idx = fileName.lastIndexOf(".");
String fileNm = fileName.substring(0, idx); // a
//확장자 구하기 => .txt
String ext = fileName.substring(idx); // .txt
//변경된 파일이름
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
// => '년원일시분초밀리초' 포멧
String today = sdf.format(d);
String result = fileNm + "_" + today + ext;
logger.info("변경된 파일명 : {}", result);
return result;
}
public String getUploadPath(HttpServletRequest request, int pathFlag) {
//업로드 경로 구하기
//업로드경로를 설정해도 이클립스에서는 다른 경로를 사용하므로, test경로를 따로사용해야하고
//직접배포했을시에는 getRealPath()를 사용하면 경로가 잘 잡히기 때문에,
//테스트경로가 따로 필요함
String path = "";
if(ConstUtil.FILE_UPLOAD_TYPE.equals("test")) { // 배포전 이클립스에서 테스트시
if(pathFlag == ConstUtil.UPLOAD_FILE_FLAG) { // 자료실
path = ConstUtil.FILE_UPLOAD_PATH_TEST;
} else if(pathFlag == ConstUtil.UPLOAD_IMAGE_FLAG) { // 상품등록
path = ConstUtil.IMAGE_FILE_UPLOAD_PATH_TEST;
}
} else { // deploy(배포시)
if(pathFlag == ConstUtil.UPLOAD_FILE_FLAG) { // 자료실
path = ConstUtil.FILE_UPLOAD_PATH; // pds_upload
} else if(pathFlag == ConstUtil.UPLOAD_IMAGE_FLAG) { // 상품등록
path = ConstUtil.IMAGE_FILE_UPLOAD_PATH; // pd_images
}
//실제 물리적인 경로 구하기
path = request.getSession().getServletContext().getRealPath(path);
}
logger.info("업로드 경로 : {}", path);
return path;
}
}
package com.pmp4.amoimproject.common;
public interface ConstUtil {
//파일 업로드 관련 상수
String FILE_UPLOAD_TYPE = "test"; // 테스트시
//String FILE_UPLOAD_TYPE = "deploy"; // 배포시
//파일 저장 경로
String TEST_FILE_PATH = "/~~/~~"; //배포 전, 절대 경로
String FILE_UPLOAD_PATH = "pd_upload";
String FILE_UPLOAD_PATH_TEST = TEST_FILE_PATH + FILE_UPLOAD_PATH;
//이미지 저장 경로
String IMAGE_FILE_UPLOAD_PATH = "img_upload";
String IMAGE_FILE_UPLOAD_PATH_TEST = TEST_FILE_PATH + IMAGE_FILE_UPLOAD_PATH;
//자료실 업로드인지, 상품 등록시 업로드인지 구분값
int UPLOAD_FILE_FLAG = 1; // 자료실 업로드
int UPLOAD_IMAGE_FLAG = 2; // 이미지 업로드
}
끝!