21.03.02 Untact프로젝트(관리자 메인화면 구현 ~ 첨부파일 업로드 및 리스팅시 섬네일 노출까지)
2021. 3. 2. 22:16ㆍJAVA/Spring & Vue APP 프로젝트(백엔드)
#NOTE
<URL vs URI>
URI는 인터넷 상의 자원을 식별하기 위한 문자열의 구성쯤으로 해석 될 수 있겠다.
URI의 한 형태인 URL은 인터넷 상의 자원 위치를 나타낸다.
URL는 URI의 한 형태로, 바꿔 말하면 URI는 URL을 포함 하는 개념이다.
<테일윈드 container의 역할>
container: @media, max-width, min-width가 걸려있음
즉, 반응형이 적용되어있는 것
그냥 con이 container의 줄임말
<select class="py-2 select-board-id">
<option value="1">공지사항</option>
<option value="2">자유게시판</option>
</select>
<script>
// select의 옵션의 value를 .val(param.boardId)값으로 바꾼다
$('.section-1 .select-board-id').val(param.boardId);
$('.section-1 .select-board-id').change(function() {
//change() : 뭔가 바뀔때마다 실행되는 함수
//location.href : 현재 페이지의 URL
location.href = '?boardId=' + this.value;
});
</script>
<change()>
바뀔때마다 실행되는 함수
<selected="selected">
select에서 디폴트로 선택되어 있는 옵션이라는 의미
<autofocus="autofocus">
<첨부파일 기능>
일반 jsp에서 첨부파일 기능을 구현하려면
많은 로직이 필요함
스프링에선 보다 적은 로직으로 구현이 가능함
input type="file"을 하려면
form 전송방식은 무조건 POST여야 한다.
또한, form 속성에 enctype="multipart/form-data" 추가해야 함
#주요 소스코드
<관리자 게시물 리스트 페이지>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ include file="../part/mainLayoutHead.jspf"%>
<section class="section-1">
<div class="bg-white shadow-md rounded container mx-auto p-8 mt-8">
<div class="flex items-center">
<select class="py-2 select-board-id">
<option value="1">공지사항</option>
<option value="2">자유게시판</option>
</select>
<script>
// select의 옵션의 value를 .val(param.boardId)값으로 바꾼다
$('.section-1 .select-board-id').val(param.boardId);
$('.section-1 .select-board-id').change(function() {
//change() : 뭔가 바뀔때마다 실행되는 함수
//location.href : 현재 페이지의 URL
location.href = '?boardId=' + this.value;
});
</script>
<div class="flex-grow"></div>
<a href="add?boardId=${param.boardId}" class="btn-primary bg-blue-500 hover:bg-blue-dark text-white font-bold py-2 px-4 rounded">글쓰기</a>
</div>
<div>
<c:forEach items="${articles}" var="article">
<div class="flex justify-between items-center mt-10">
<span class="font-light text-gray-600">${article.regDate}</span>
<a href="list?boardId=${article.boardId}" class="px-2 py-1 bg-gray-600 text-gray-100 font-bold rounded hover:bg-gray-500">${article.extra__boardName}</a>
</div>
<div class="mt-2">
<a href="detail?id=${article.id}" class="text-2xl text-gray-700 font-bold hover:underline">${article.title}</a>
<p class="mt-2 text-gray-600">${article.body}</p>
<div>
<c:if test="${article.extra__thumbImg != null}">
<img src="${article.extra__thumbImg}" alt="" />
</c:if>
</div>
</div>
<div class="flex justify-between items-center mt-4">
<a href="detail?id=${article.id}" class="text-blue-500 hover:underline">자세히 보기</a>
<div>
<a href="detail?id=${article.id}" class="flex items-center">
<img src="https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=731&q=80" alt="avatar" class="mx-4 w-10 h-10 object-cover rounded-full">
<h1 class="text-gray-700 font-bold hover:underline">${article.extra__writer}</h1>
</a>
</div>
</div>
</c:forEach>
</div>
</div>
</section>
<%@ include file="../part/mainLayoutFoot.jspf"%>
<genFile 테이블>
# 파일 테이블 추가
CREATE TABLE genFile (
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, # 번호
regDate DATETIME DEFAULT NULL, # 작성날짜
updateDate DATETIME DEFAULT NULL, # 갱신날짜
delDate DATETIME DEFAULT NULL, # 삭제날짜
delStatus TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, # 삭제상태(0:미삭제,1:삭제)
relTypeCode CHAR(50) NOT NULL, # 관련 데이터 타입(article, member)
relId INT(10) UNSIGNED NOT NULL, # 관련 데이터 번호
originFileName VARCHAR(100) NOT NULL, # 업로드 당시의 파일이름
fileExt CHAR(10) NOT NULL, # 확장자
typeCode CHAR(20) NOT NULL, # 종류코드 (common)
type2Code CHAR(20) NOT NULL, # 종류2코드 (attatchment)
fileSize INT(10) UNSIGNED NOT NULL, # 파일의 사이즈(byte)
fileExtTypeCode CHAR(10) NOT NULL, # 파일규격코드(img, video)
fileExtType2Code CHAR(10) NOT NULL, # 파일규격2코드(jpg, mp4)
fileNo SMALLINT(2) UNSIGNED NOT NULL, # 파일번호 (1)
fileDir CHAR(20) NOT NULL, # 파일이 저장되는 폴더명
PRIMARY KEY (id),
KEY relId (relId,relTypeCode,typeCode,type2Code,fileNo)
);
<글쓰기 시 첨부파일 업로드>
<AdmArticleController.java>
@RequestMapping("/adm/article/doAdd")
@ResponseBody
public ResultData doAdd(@RequestParam Map<String, Object> param, HttpServletRequest req, MultipartRequest multipartRequest) {
................................[생략]
ResultData addArticleRd = articleService.addArticle(param);
// addArticleRd map의 body에서 key값이 id인 것을 가져와라
int newArticleId = (int) addArticleRd.getBody().get("id");
//MultipartRequest : 첨부파일 기능 관련 요청
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap(); //MultipartRequest로 들어온 map 정보를 가져오기
//fileMap.keySet() : file__article__0__common__attachment__1
for (String fileInputName : fileMap.keySet()) {
//fileInputName : file__article__0__common__attachment__1
MultipartFile multipartFile = fileMap.get(fileInputName);
if(multipartFile.isEmpty() == false) {
//저장할 파일관련 정보를 넘김
genFileService.save(multipartFile, newArticleId);
}
}
return addArticleRd;
}
--------------------------------------------------------------------------------
<GenFileService.java>
public ResultData save(MultipartFile multipartFile, int relId) {
String fileInputName = multipartFile.getName();
//'file__article__0__common__attachment__1'를 "__" 기준으로 쪼갠다.
// 0 1 2 3 4 5
String[] fileInputNameBits = fileInputName.split("__");
if (fileInputNameBits[0].equals("file") == false) {
return new ResultData("F-1", "파라미터명이 올바르지 않습니다.");
}
// getSize() : 파일 사이즈를 가져오는 명령어
int fileSize = (int) multipartFile.getSize();
// 파일 사이즈가 0이거나 0보다 작으면
if (fileSize <= 0) {
return new ResultData("F-2", "파일이 업로드 되지 않았습니다.");
}
String relTypeCode = fileInputNameBits[1];
String typeCode = fileInputNameBits[3];
String type2Code = fileInputNameBits[4];
int fileNo = Integer.parseInt(fileInputNameBits[5]);
String originFileName = multipartFile.getOriginalFilename();
String fileExtTypeCode = Util.getFileExtTypeCodeFromFileName(multipartFile.getOriginalFilename());
String fileExtType2Code = Util.getFileExtType2CodeFromFileName(multipartFile.getOriginalFilename());
String fileExt = Util.getFileExtFromFileName(multipartFile.getOriginalFilename()).toLowerCase();
if (fileExt.equals("jpeg")) {
fileExt = "jpg";
} else if (fileExt.equals("htm")) {
fileExt = "html";
}
String fileDir = Util.getNowYearMonthDateStr();
ResultData saveMetaRd = saveMeta(relTypeCode, relId, typeCode, type2Code, fileNo, originFileName,
fileExtTypeCode, fileExtType2Code, fileExt, fileSize, fileDir);
int newGenFileId = (int) saveMetaRd.getBody().get("id");
// 새 파일이 저장될 폴더(io파일) 객체 생성
String targetDirPath = genFileDirPath + "/" + relTypeCode + "/" + fileDir;
java.io.File targetDir = new java.io.File(targetDirPath);
// 새 파일이 저장될 폴더가 존재하지 않는다면 생성
if (targetDir.exists() == false) {
targetDir.mkdirs();
}
String targetFileName = newGenFileId + "." + fileExt;
String targetFilePath = targetDirPath + "/" + targetFileName;
// 파일 생성(업로드된 파일을 지정된 경로로 옮김)
try {
multipartFile.transferTo(new File(targetFilePath));
} catch (IllegalStateException | IOException e) {
return new ResultData("F-3", "파일저장에 실패하였습니다.");
}
return new ResultData("S-1", "파일이 생성되었습니다.", "id", newGenFileId, "fileRealPath", targetFilePath, "fileName", targetFileName);
}
<게시물 리스팅 시 첨부파일1 섬네일 노출>
<AdmArticleController.java>
@RequestMapping("/adm/article/list")
//@ResponseBody
public String showList(HttpServletRequest req, @RequestParam(defaultValue = "1") int boardId, String searchKeywordType, String searchKeyword, @RequestParam(defaultValue = "1") int page) {
..................................[생략]
/* 각 article에 달려있는 첨부파일 섬네일 가져오기 시작 */
for ( Article article : articles ) {
//String relTypeCode, int relId, String typeCode, String type2Code, int fileNo
GenFile genFile = genFileService.getGenFile("article", article.getId(), "common", "attachment", 1);
if ( genFile != null ) {
//img의 url을 가져오기
article.setExtra__thumbImg(genFile.getForPrintUrl());
}
}
/* 각 article에 달려있는 첨부파일 섬네일 가져오기 끝 */
'JAVA > Spring & Vue APP 프로젝트(백엔드)' 카테고리의 다른 글
21.03.05 Untact프로젝트(게시물 수정 페이지에서 기존에 업로드된 파일 노출, 수정, 삭제, 용량 표시 등 ) (0) | 2021.03.05 |
---|---|
21.03.04 Untact프로젝트(파일업로드 ajax방식으로 처리, 파일 반복문 처리, 파일 저장까지) (0) | 2021.03.04 |
21.02.28~03.01 Untact프로젝트(페이징~history.back(), location.replace() 도입까지) (0) | 2021.03.01 |
21.02.25~27 Untact프로젝트(회원가입~인터셉터 도입까지) (0) | 2021.02.27 |
21.02.24 Untact프로젝트(게시물 삭제, 수정, 현재날짜 적용 등) (0) | 2021.02.24 |