스프링 첨부파일 업로드
2022. 1. 24. 22:12ㆍFramework
반응형
첨부파일이 넘어올 경우 두 단계를 거쳐
1서버의 어떤 폴더에 직접적으로 저장
2db에도 저장
파일 자체를 넘길 시 enctype을 지정해야 함
enctype="multipart/form-data"
지정하지 않으면 파일명 즉 문자열만 넘어감
먼저 mvn리파지토리에서
commons-fileupload검색 후
Apache Commons FileUpload와
Apache Commons IO 디펜던시 복사

pom.xml에 디벤던시즈 안에 붙여넣기

스프링에서 제공하는 클래스라서 직접 수정 불가
어노테이션등록이 불가함으로 xml으로 등록
용량제한값을 무제한으로 줄 수 있는데
100메가로 설정
maxUploadSize, maxInMemorySize를 작성해서
용량제한값 설정. 밸류에는 바이트크기로 작성.

<!-- 파일업로드 관련 빈 등록 -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<property name="maxUploadSize" value="100000000" />
<property name="maxInMemorySize" value="100000000" />
</bean>
@vo
@NoArgsConstructor
@Setter
@Getter
@ToString
public class Board {
private int boardNo;
private String boardTitle;
private String boardWriter;
private String boardContent;
private String originName;
private String changeName; // "resources/uploadFiles/xxx.jpg" 파일경로
private int count;
private String createDate;
private String status;
}
@Controller
// ** 만약 다중파일 업로드 시?
// 여러개의 input type="file"요소에 동일한 키값으로 부여 (ex. name="upfile")
// MultipartFile[] upfile로 전달받기. (0번 인덱스부터 차곡차곡 첨부파일이 담김)
// 반복문 돌려가면서 파일명 수정 작업 후 서버 업로드 작업
// 이때 Board테이블에 첨부파일을 같이 보관할 수 없음. 하나의 게시글에 여러 개의 첨부파일일 경우 첨부파일 별도 보관하는 테이블 생성
@RequestMapping("insert.bo")
public String insertBoard(Board b, MultipartFile upfile, HttpSession session, Model model) {
// System.out.println(b);
// System.out.println(upfile); // 첨부파일이 있든 없든 생성된 객체(다만 filename에 원본명이 있냐?)
// 전달된 파일이 있을 경우 => 파일명 수정 작업 후 서버 업로드 => 원본명, 서버업로드된 경로를 b에 담기
if(!upfile.getOriginalFilename().equals("")) {
// 파일명 수정 작업 후 서버에 업로드 ("flower.png" => "20220118103646521.png")
/*
String originName = upfile.getOriginalFilename(); // "flower.png"
String currentTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); // "202201181036"
int ranNum = (int)(Math.random() * 90000 + 10000); // 46521 (5자리랜덤값)
String ext = originName.substring(originName.lastIndexOf(".")); // ".png"
String changeName = currentTime + ranNum + ext;
// 업로드한 파일을 보관할 폴더의 물리적인 경로 알아내어 파일을 changeName의 이름으로 저장
// 물리적인 경로는 세션객체를 먼저 얻어내서 겟리얼패스메소드까지 호출
String savePath = session.getServletContext().getRealPath("/resources/uploadFiles/");
try {
upfile.transferTo(new File(savePath + changeName));
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
*/
String changeName = saveFile(upfile, session);
// 세이브파일메소드를 업파일과 세션을 전달해서 호출. 리턴된 것을 체인지네임에 담기
// 원본명, 서버업로드된경로를 Board b에 이어서 담기
b.setOriginName(upfile.getOriginalFilename());
b.setChangeName("resources/uploadFiles/" + changeName); // 어떤 경로에 저장되었는지도 같이 담기
}
// 넘어온 첨부파일 있을 경우 b : 제목, 작성자, 내용, 파일원본명, 파일저장경로
// 넘어온 첨부파일 없을 경우 b : 제목, 작성자, 내용
// 각 필드에 담긴 내용 이제 db에 인설트하러
int result = bService.insertBoard(b);
if(result > 0) {
session.setAttribute("alertMsg", "게시글 등록 성공");
return "redirect:list.bo";
} else {
model.addAttribute("errorMsg", "게시글 등록 실패");
return "common/errorPage";
}
}
// 넘어온 첨부파일을 파일명 수정 작업 후 보관폴더에 업로드해주는 메소드
public String saveFile(MultipartFile upfile, HttpSession session) {
String originName = upfile.getOriginalFilename(); // "flower.png"
String currentTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); // "202201181036"
int ranNum = (int)(Math.random() * 90000 + 10000); // 46521 (5자리랜덤값) 10000부터 90000개 즉 10000~99999 중 랜덤값
String ext = originName.substring(originName.lastIndexOf(".")); // ".png" 뒤에서부터 .기준으로 문자열찾기 => 확장자 알아내는 법
String changeName = currentTime + ranNum + ext; // 어떤 파일명으로 보관할지 세팅완성
// 업로드한 파일을 보관할 폴더의 물리적인 경로 알아내어 파일을 changeName의 이름으로 저장
// 물리적인 경로는 세션객체로부터 겟리얼패스메소드(jsp스코프중에 애플리케이션객체 얻어내기) 호출
// 겟리얼패스메소드(보관할 폴더의 물리적인 경로 작성)
String savePath = session.getServletContext().getRealPath("/resources/uploadFiles/");
try {
upfile.transferTo(new File(savePath + changeName));
// 트랜스펄투: 파일객체의 경로와 체인지네임으로 실제 업로드하기 위한 메소드
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
return changeName;
}
@RequestMapping("delete.bo")
public String deleteBoard(int bno, String filePath, HttpSession session, Model model) {
// String filePath : boardDetailView.jsp상에 form태그 안에 filePath라는 키값으로 changeName(파일경로)밸류값을 넘긴 것을 받음
// filePath : "resource/xxxx/xxxxx.jpg"
int result = bService.deleteBoard(bno);
if(result > 0) {
// 첨부파일이 있을 경우 = > 서버에 업로드된 파일 삭제
if(!filePath.equals("")) {
new File(session.getServletContext().getRealPath(filePath)).delete();
// 파일을 삭제하려면 파일이 저장되어있는 물리적인 경로가 필요함. 먼저 세션객체 필요
// 세션객체로부터 겟서블릿컨텍스트메소드를 이용하면 jsp스코프중에 애플리케이션내장객체를 얻을 수 있고
// 겟리얼패스메소드를 이용하면 물리적인 경로를 얻애낼 수 있음.
// 물리적인경로를 알아고자하는 일부경로를 괄호안에 제시하면 파일경로를 알아낼 수 있음.
// 일부경로인 filePath를 제시하면 C드라이브에서부터 그 경로를 따라가서 파일을 찾음
// 뉴파일. 찾아진 파일을 파일객체로 생성해서. 파일객체에서 제공하는 딜리트메소드를 호출하면 그 파일이 삭제됨
}
session.setAttribute("alertMsg", "게시글 삭제 성공");
return "redirect:list.bo";
} else {
model.addAttribute("errorMsg", "게시글 삭제 실패");
return "common/errorPage";
}
}
@RequestMapping("update.bo")
public String updateBoard(Board b, MultipartFile reupfile, HttpSession session, Model model) {
// 새로 넘어온 첨부파일 있을 경우
if(!reupfile.getOriginalFilename().equals("")) {
// 기존에 첨부파일이 있었을 경우 => 기존 첨부파일 삭제
if(b.getOriginName() != null) {
new File(session.getServletContext().getRealPath(b.getChangeName())).delete();
// 세션객체로부터 겟서블릿컨텍스트메소드를 이용하면 jsp스코프중에 애플리케이션내장객체를 얻을 수 있고
// 겟리얼패스메소드를 이용하면 물리적인 경로를 얻애낼 수 있음.
// 물리적인경로를 알아고자하는 일부경로를 괄호안에 제시하면 파일경로를 알아낼 수 있음.
// 뉴파일. 찾아진 파일을 파일객체로 생성해서. 파일객체에서 제공하는 딜리트메소드를 호출하면 그 파일이 삭제됨
}
// 새로 넘어온 첨부파일 서버 업로드
// originName을 changeName으로 변경하면서 업로드하기위해 saveFile메소드호출
String changeName = saveFile(reupfile, session);
// b에 새로 넘어온 첨부파일에 대한 원본명, 저장경로 담기
b.setOriginName(reupfile.getOriginalFilename());
b.setChangeName("resources/uploadFiles/" + changeName);
}
/*
* b에 boardNo, boardTitle, boardContent는 무조건 담겨있는데
*
* 1. 새로 첨부된 파일 x, 기존 첨부 파일 x
* => originName : null, changeName : null
*
* 2. 새로 첨부된 파일 x, 기존 첨부 파일 o
* => originName : 기존첨부파일원본명, changeName : 기존첨부파일저장경로
*
* 3. 새로 첨부된 파일 o, 기존 첨부 파일 x
* => 새로 전달된 파일 서버에 업로드
* => originName : 새로운첨부파일원본명, changeName : 새로운첨부파일저장경로
*
* 4. 새로 첨부된 파일 o, 기존 첨부 파일 o
* => 기존의 파일 삭제
* => 새로 전달된 파일 서버에 업로드
* => originName : 새로운첨부파일원본명, changeName : 새로운첨부파일저장경로
*/
int result = bService.updateBoard(b);
if(result > 0) {
session.setAttribute("alertMsg", "게시글 수정 성공");
return "redirect:detail.bo?bno=" + b.getBoardNo();
} else {
model.addAttribute("alertMsg", "게시글 수정 실패");
return "common/errorPage";
}
}
@boardDetailView
<c:if test="${ loginUser.userId eq b.boardWriter }">
<div align="center">
<!-- 이렇게 기술하는 경우 get방식으로 url에 노출됨
=> 의도치않은 수정,삭제가 발생할 가능성
<a class="btn btn-primary" href="updateForm.bo?bno=${ b.boardNo }">수정하기</a>
<a class="btn btn-danger" href="delete.bo?bno=${ b.boardNo }">삭제하기</a>
-->
<a class="btn btn-primary" onclick="postFormSubmit(1)">수정하기</a>
<a class="btn btn-danger" onclick="postFormSubmit(2)">삭제하기</a>
</div><br><br>
<form id="postForm" action="" method="post">
<input type="hidden" name="bno" value="${ b.boardNo }">
<input type="hidden" name="filePath" value="${ b.changeName }">
</form>
<script>
function postFormSubmit(num) {
if(num == 1) {
$("#postForm").attr("action", "updateForm.bo").submit();
} else {
$("#postForm").attr("action", "delete.bo").submit();
}
}
</script>
</c:if>
@boardUpdateForm
<c:if test="${ !empty b.originName }">
현재 업로드된 파일 :
<a href="${ b.changeName }" download="${ b.originName }">${ b.originName }</a>
<input type="hidden" name="originName" value="${ b.originName }">
<input type="hidden" name="changeName" value="${ b.changeName }">
</c:if>
반응형
'Framework' 카테고리의 다른 글
ajax vo객체 한 행/여러 행 조회, json을 gson으로 (0) | 2022.01.25 |
---|---|
스프링 ajax 사용법 (응답데이터 하나/다수일경우) (0) | 2022.01.25 |
비밀번호 암호화(Bcrypt방식)로 회원가입, 로그인 작업 (0) | 2022.01.17 |
Lombok 롬복 설치 (0) | 2022.01.15 |
스프링 한글 깨짐 현상 인코딩 작업 (UTF-8) (0) | 2022.01.14 |