20230822 _[55일차]_01. admin-notice 페이지 및 파일업로드
adminMapper에서 애초에 등급이 5 이상인 데이터만 불러오도록 수정해줄거다.
그런데 부등호를 괄호로 인식해서 오류나 날 수 있으니 아래처럼 CDATA 처리필요
<![CDATA[ ~~~ ]]> 내부의 것들은 다 문자열로 처리하라는 의미
* 주의 *
주석처리하는 경우에도 꺽쇠괄호를 쓰기때문에 아래처럼 해주면 에러가 날 수도 있다.
웬만하면 아예 쿼리문 바깥쪽에 써주기
이제 드디어 파일업로드 한다!!!
책에서는 요만큼을 추가해야 사용할 수 있는데
FileUpload – Home (apache.org)
FileUpload – Home
Commons FileUpload The Commons FileUpload package makes it easy to add robust, high-performance, file upload capability to your servlets and web applications. FileUpload parses HTTP requests which conform to RFC 1867, "Form-based File Upload in HTML". That
commons.apache.org
Maven Repository: commons-fileupload » commons-fileupload » 1.5 (mvnrepository.com)
우리가 배울 방식은 스프링에 내장되어 있는걸 사용할 예정
adminMapper에서 noticeWrite과정에서(글쓰기과정) nrealfile 추가
A라는 사람이 1.png라고 올리고
B라는 사람이 1.png라고 올린다면 이름이 겹친다
아래의 예시처럼 원래의 파일이름과 서버에 업로드되는 파일이름을 다르게 해줄거다
=> uuid 이용 or 파일이름+년월일시분초+id 붙여버리기
다중파일 업로드는 하나의 파일을 올릴때의 과정(컨트롤러의 과정_upfile)을 계속 반복시키는거
그래서 배열처리됨..?
파일넣어서 업로드 해보면 (파일유무가 없다고 뜨는데 왜인지는 모르겠다. 조금 지나니까 해결됐다)
실제 경로는 webapp 내부의 upload 폴더에
.transferTo (newFileName) : 실제 파일의 데이터(경로와 이름)를 newFileName에 저장.
(클라이언트의 파일 정보를 서버에 저장해준거)
파일경로가 String 타입이라 file 형태로 변경해줌
upload 폴더 위치를 static -> webapp으로 옮겨줌
근데 아래처럼 파일유무가 제대로 작동을 안하는데
그 이유는 아래에서 RequestParam 부분에서 upfile을 잡아가기 때문에 뒤쪽에서는 잡아낼 수 없다고 한다.
일단 업로드한 이미지 파일은 잘 업로드 되었다.
새로운 객체생성없이 처리하려면 아래처럼 해줘도 된다.
잘된다.
만약에 중복되는 이름으로 업로드를 해본다면 일단 지금은 덮어쓴다.
아예 업로드 할때 다른 이름으로 저장되도록 설정해줄건데 방법은 여러가지
> 파일명 + 날짜 + ID + .파일확장자
> UUID + 파일명 + .확장자
> UUID + 파일명 + ID + .확장자
두번째꺼로 해보자
파일명 앞에 랜덤uuid 를 붙인 이름으로 파일을 업로드 해주면
파일명 앞에 uuid가 추가된 이름으로 저장된다.
이번에는 날짜를 이름앞에 추가해보자
날짜 뽑아서 realFileName을 다시 정해줬다.
파일업로드를 해보면
앞에 날짜가 붙은채로 저장이된다.
session에서 id를 뽑아와서 붙여봐도 된다.
아니면 LocalDateTime 사용해보자
java.time.LocalDateTime
다시 이미지 파일을 올려보면
날짜가 붙는 형태는 똑같다.
Date (java.util.Date)
vs
LocalDateTime (java.time.LocalDate) : API가 명확하고, 불변성이 보장되어 더 많이 사용
LocalDate & LocalDateTime => Java8부터 등장
최종적으로는 아래처럼 하기로 결정
그럼 아래처럼 '날짜 + UUID + 실제파일명' 으로 저장이된다.
@PostMapping("/noticeWrite")
public String noticeWrite(@RequestParam ("upFile") MultipartFile upfile, @RequestParam Map<String, Object> map) {
// {title=제목쓰고, content=글을쓰고 글쓰기버튼 누르면, upFile=}
// System.out.println(map);
// 2023-08-22 요구사항확인
if(!upfile.isEmpty()) {
// 저장할 경로면 뽑기 request 뽑기
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
String path = request.getServletContext().getRealPath("/upload");
//System.out.println("실제 경로 : " + path);
// upfile 정보보기
//System.out.println(upfile.getOriginalFilename());
//System.out.println(upfile.getSize());
//System.out.println(upfile.getContentType());
// 진짜로 파일 업로드 하기 경로 + 저장할 파일명
// String타입의 경로를 file형태로 바꿔주겠습니다.
// File filePath = new File(path);
// 중복이 발생할 수 있기때문에 ____ 파일명 + 날짜 + ID + .파일확장자
// UUID + 파일명 + .확장자
// UUID + 파일명 + ID + .확장자
UUID uuid = UUID.randomUUID();
// String realFileName = uuid.toString() + upfile.getOriginalFilename();
// 날짜 뽑기 SimpleDateFormat
// Date date = new Date();
// SimpleDateFormat sdf = new SimpleDateFormat("YYYYMMddHHmmss");
// String dateTIme = sdf.format(date);
// realFileName = dateTIme.toString() + upfile.getOriginalFilename();
// 다른 날짜 뽑기 형식
LocalDateTime ldt = LocalDateTime.now();
String format = ldt.format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
// 날짜 + UUID + 실제 파일명으로 사용하겠습니다.
String realFileName = format + uuid.toString() + upfile.getOriginalFilename();
File newFileName = new File(path, realFileName);
//이제 파일 올립니다.
try {
upfile.transferTo(newFileName);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("저장끝.");
}
@PostMapping("/noticeWrite")
public String noticeWrite(@RequestParam ("upFile") MultipartFile upfile, @RequestParam Map<String, Object> map) {
if(!upfile.isEmpty()) {
// 저장할 경로면 뽑기 request 뽑기
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
String path = request.getServletContext().getRealPath("/upload");
UUID uuid = UUID.randomUUID();
// 다른 날짜 뽑기 형식
LocalDateTime ldt = LocalDateTime.now();
String format = ldt.format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
// 날짜 + UUID + 실제 파일명으로 사용하겠습니다.
String realFileName = format + uuid.toString() + upfile.getOriginalFilename();
File newFileName = new File(path, realFileName);
//이제 파일 올립니다.
try {
upfile.transferTo(newFileName);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("저장끝.");
}
* FileCopyUtils => 스프링부트 유틸패키지에서 지원
방법이 두가지 (둘중 하나 사용하면 된다.)
책 p. 500 ~ 에 보면 ajax로 파일업로드 하는방법이 나와있다.
p. 530 ~ 다운로드
p. 548 ~ 메소드만 불러와서 삭제
참고참고
이제 파일의 원래이름과 업로드이름을 각각 map에 넣어준다.
이때 키값은 mapper에서 미리 써줬던 이름하고 맞춰줘야 한다.
mapper에 따옴표 사이에 공백이 있어서 에러가 났다 ㅎㅎㅎㅎ
그럼 이미지 파일을 올렸을때
이제 db까지 잘 올라간다.
업로드 파일명이 길어서 오류가 나는 경우도 있으니 하이디에서 길이도 바꿔줬다.
이제 관리자페이지 말고 원래 게시판으로 돌아와서
notice.jsp 마저 완성해주고 mapper 수정해준다.
nno의 역순으로 10개의 공지글만 불러온다.
ndel을 활용해서 ndel이 1인 경우만 불러와보자
mapper의 쿼리문에 ndel이 1일때만 불러오는 조건문 추가
잘 불러와진다.
이제 공지글을 눌렀을때 제대로 주소가 잡히도록 jsp를 수정해주자
해당하는 nno를 잡아 noticeDetail 페이지로 이동할 수 있도록 수정해주고
이제 컨트롤러에서 데이터값을 가져오는 로직 만들자
jsp에서 nno를 잡아와 이걸 매개변수로 db에서 결과값을 가져와서 model에 붙여 jsp로 보낼예정
서비스에서도 nno를 매개변수로 detail() 메소드 실행
DAO에서도 nno를 매개변수로 detail() 메소드 실행 => mapper에서 쿼리문 실행
공지글을 클릭하면 잘 불러온다.
이미지도 불러와보자
notice.jsp에서
일단 m_no 로 불러왔던 글쓴이도 m_name으로 불러와보자
이름 불러왔다 야호
업로드된 파일명에서 파일 확장자만 뽑아내보자.
substring() 메소드로 문자열을 잘라서 원하는 값만 뽑아내보자.
아래 두개는 같은의미다.
아래처럼 잘라내 주면
png만 잘라낼 수 있다.
아니면 contains() 메소드로 해당 확장자를 포함하고 있는지의 여부를 확인
png를 포함하고 있다면 true값을 반환
아니면 토큰처리
다시 토큰방식 조건문 정리
${detail.nrealfile} 를 .(콤마)를 기준으로 잘라낸 값들을 차례차례 tocken이라는 변수에 넣는 반복문이라고 생각하면 될듯
status.count : 반복횟수
status.first : tocken들 중 첫번째 값
status.last : tocken들 중 마지막 값
==> 20230822104055765d889e-912b-4e19-8a42-6165bd8652b9mazayong . png
콤마를 기준으로 잘랐을때 마지막 값은 확장자인 png가 된다.
확장자를 값으로 가지는 tocken에 대해서, 이 값이 png, jpg, jpeg... 등등 이미지 확장자와 같다면
이미지 파일을 jsp에 띄운다.
전체 토큰을 한번더 div로 묶어줬다.
<header class="masthead">
<div class="container">
<div id="detail">
<div id="detailH">${detail.ntitle }</div>
<div id="detailN_no" style="height: 0px; visibility: hidden;">${detail.nno }</div>
<div id="detailIdDate">
<div id="detailID">${nwriter }</div>
<div id="detailDate">${detail.ndate }</div>
</div>
<div id="detailContent">${detail.ncontent }</div>
실제 파일명 : norifile=img.png / 파일명 : ${detail.nrealfile}
<c:set var="file">fn:length(detail.nrealfile)</c:set>
<c:set var="file" value="${fn:length(detail.nrealfile)}"/>
<br> ${fn:substring(detail.nrealfile, file-3, file)}
${fn:contains(detail.nrealfile, '.png')}
<br>
<div class="file=form">
<c:forTokens var="token" items="${detail.nrealfile}" delims="." varStatus="status">
<c:if test="${status.last }">
<c:choose>
<c:when
test="${token eq 'png' || token eq 'jpg'
|| token eq 'jpeg' || token eq 'gif' || token eq 'bmp'}">
<img alt="" src="../upload/${detail.nrealfile }">
</c:when>
<c:otherwise>
<button>다운로드</button>
</c:otherwise>
</c:choose>
</c:if>
</c:forTokens>
</div>
<br>
</div>
<button class="btn btn-primary xi-view-list" onclick="location.href='./multiboard'">멀티보드로</button>
</div>
</div>
</header>
코드 정리해주면
아래 마자용 이미지 파일은 png에 해당되어 조건문을 통과해 브라우저에 출력
notice.jsp (공지게시판) 화면에도 이미지 유무를 아이콘으로 표시해보자.
부트스트랩 아이콘을 사용하려면 아래태그 추가해줘야한다.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.0/font/bootstrap-icons.css">
아이콘을 notice페이지의 공지글제목 옆에 넣어준다.
이미지파일이 업로드 되어있다면 title 옆에 이미지모양 아이콘이 뜨도록 만들어줬다.
아이콘 하나더 넣어주면
이미지 파일이 있을때는 이미지 아이콘이, 아닐때는 글 아이콘이 뜬다.
토큰을 활용해보면
이미지파일이 있을떄는 이미지 아이콘이
그 외 파일이(이미지확장자x) 있을때는 글 아이콘이 뜨도록 해줬다.
noticeDeatil.jsp 에서
버튼에 onclick기능과 함께 이동할 주소 입력해준다. 주소뒤에 파일명을 붙여줬다.
주소창에 요렇게 뜬다.
이제 요 페이지를 만들어주자.
이제 이 주소창에 있는 파일이름을 잡아와서 다운로드 처리할예정
값을 잘 잡아와 콘솔창에 뜨는거 확인
이제 경로를 매번 불러오기보다 Util에다가 필요한것들 미리 넣어주고 사용하자.
경로 얻어오기
public HttpServletRequest getCurrentRequest() {
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
}
public HttpServletResponse getCurrentResponse() {
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
}
// 세션 얻어오기
public HttpSession getSession() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();
}
// 업로드 폴더까지의 경로 얻어오기
public String uploadPath() {
return getCurrentRequest().getServletContext().getRealPath("/upload");
}
이제 util에서 경로 가져와서 path에 넣어주고,
octet-stream 이게뭘까
=>8비트 단위의 binary data (특별히 표현할 수 있는 프로그램이 존재하지 않는 데이터의 경우 기본값으로 사용)
try/catch 로 예외처리 해주고
메소드 타입 String => void 로 변경, response에 정보들 추가
이제 다운로드 버튼을 누르면 파일다운로드가 된다!!
다운로드 되는 파일이름이 너무길다.
아래처럼 업로드경로는 빼고 filename만 남겨주면
다운로드 파일명만 요렇게 온다.
요것도 길다. filename 을 db로 보내서 해당하는 파일의 순수파일명만 가지고 오자.
결과를 oriFileName으로 받아 이걸 다운로드시 뜨는 이름으로 넣어준다.
서비스에서도 실행
DAO에서도 실행
mapper에서 쿼리문 작성해준다.
순수파일명으로만 다운로드가 된다.
메일보내기 만들어보자.
일단 아웃룩계정 만들었다.
gradle에는 아랫부분 추가
adminController에서
main.jsp 생성
이메일창 만들었다.
메일보내기 페이지 완성