feat: 공지사항 파일업로드 처리

dev
minuk926 3 years ago
parent 48999de67a
commit d2469ee7ca

@ -0,0 +1,83 @@
package com.xit.biz.ctgy.controller;
import com.xit.biz.cmm.dto.CmmFileDto;
import com.xit.biz.cmm.entity.CmmFileMst;
import com.xit.biz.ctgy.dto.MinInfoBoard680Dto;
import com.xit.biz.ctgy.entity.MinInfoBoard680;
import com.xit.biz.ctgy.service.ICtgyFileService;
import com.xit.core.api.IRestResponse;
import com.xit.core.api.RestResponse;
import com.xit.core.util.AssertUtils;
import com.xit.core.util.Checks;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Nonnull;
import java.util.List;
@Tag(name = "CtgyFileMgtController", description = "파일 관리")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/ctgy/file")
public class CtgyFileMgtController {
private final ICtgyFileService fileService;
@Operation(summary = "파일 조회" , description = "등록된 파일 조회")
@GetMapping("/{id}")
public ResponseEntity<? extends IRestResponse> findFiles(@PathVariable("inCode") @NonNull final Long inCode) {
AssertUtils.isTrue(!Checks.isEmpty(inCode), "대상 게시글[inCode]을 선택해 주세요.");
return RestResponse.of(fileService.findFiles(inCode));
}
//@Operation(summary = "파일 저장" , description = "파일 저장")
//@PostMapping()
// public ResponseEntity<? extends IRestResponse> saveFiles2(CmmFileMst cmmFileMst, @RequestParam("files") MultipartFile[] files) {
// AssertUtils.isTrue(!Checks.isEmpty(cmmFileMst), "파일 정보가 존재하지 않습니다.");
// AssertUtils.isTrue(!Checks.isEmpty(cmmFileMst.getFileCtgCd()), "파일 구분 코드[fileCtgCd] 정보가 존재하지 않습니다.");
// AssertUtils.isTrue(!Checks.isEmpty(cmmFileMst.getFileBizId()), "파일 업무 ID[fileBizId] 정보가 존재하지 않습니다.");
// AssertUtils.isTrue(!Checks.isEmpty(files), "대상 파일이 존재하지 않습니다.");
// return RestResponse.of(fileService.saveFiles(cmmFileMst, files));
//
// RedirectAttributes redirectAttributes
// redirectAttributes.addFlashAttribute("message",
// "You successfully uploaded " + file.getOriginalFilename() + "!");
//
// return "redirect:/";
// }
@Operation(summary = "파일 저장" , description = "파일 저장")
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<? extends IRestResponse> saveFiles(@Nonnull MinInfoBoard680Dto dto) {
AssertUtils.isTrue(!Checks.isEmpty(dto), "파일 정보가 존재하지 않습니다.");
// AssertUtils.isTrue(!Checks.isEmpty(cmmFileDto.getFileCtgCd()), "파일 구분 코드[fileCtgCd] 정보가 존재하지 않습니다.");
// AssertUtils.isTrue(!Checks.isEmpty(cmmFileDto.getFileBizId()), "파일 업무 ID[fileBizId] 정보가 존재하지 않습니다.");
// AssertUtils.isTrue(!Checks.isEmpty(cmmFileDto.getFiles()), "대상 파일이 존재하지 않습니다.");
// CmmFileMst cmmFileMst = CmmFileMst.builder()
// .fileMstId(cmmFileDto.getFileMstId())
// .fileCtgCd(cmmFileDto.getFileCtgCd())
// .fileBizId(cmmFileDto.getFileBizId())
// .build();
MinInfoBoard680 minInfoBoard680 = MinInfoBoard680.builder()
.inCode(dto.getInCode())
.inBgubun(dto.getInBgubun())
.inTitle(dto.getInTitle())
.inContents(dto.getInContents())
.build();
//fileService.saveFiles(minInfoBoard680, dto.getFiles());
return RestResponse.of(fileService.saveFiles(minInfoBoard680, dto.getFiles()));
// RedirectAttributes redirectAttributes
// redirectAttributes.addFlashAttribute("message",
// "You successfully uploaded " + file.getOriginalFilename() + "!");
//
// return "redirect:/";
}
}

@ -5,8 +5,10 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.springframework.web.multipart.MultipartFile;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Transient;
import java.io.Serializable; import java.io.Serializable;
@Schema(name = "MinInfoBoard680Dto", description = "공지사항") @Schema(name = "MinInfoBoard680Dto", description = "공지사항")
@ -58,4 +60,8 @@ public class MinInfoBoard680Dto implements Serializable {
@Schema(required = false, title = "기타", example = " ", description = "Input Description...") @Schema(required = false, title = "기타", example = " ", description = "Input Description...")
private String inEtc; private String inEtc;
//@Transient
@Schema(required = false, title = "파일", example = " ", description = "파일")
private MultipartFile[] files;
} }

@ -1,17 +1,19 @@
package com.xit.biz.ctgy.entity; package com.xit.biz.ctgy.entity;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.*;
import lombok.NoArgsConstructor;
import javax.persistence.*; import javax.persistence.*;
import java.io.Serializable; import java.io.Serializable;
@Schema(name = "MinInfoBoard680", description = "공지사항") @Schema(name = "MinInfoBoard680", description = "공지사항")
@NoArgsConstructor
@Data
@Entity
@Table(name = "min_info_board680", schema = "", catalog = "") @Table(name = "min_info_board680", schema = "", catalog = "")
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
public class MinInfoBoard680 implements Serializable { public class MinInfoBoard680 implements Serializable {
private static final long SerialVersionUID = 1L; private static final long SerialVersionUID = 1L;
@ -71,6 +73,4 @@ public class MinInfoBoard680 implements Serializable {
@Schema(required = false, title = "기타", example = " ", description = "Input Description...") @Schema(required = false, title = "기타", example = " ", description = "Input Description...")
@Column(name = "in_etc", nullable = true) @Column(name = "in_etc", nullable = true)
private String inEtc; private String inEtc;
} }

@ -0,0 +1,18 @@
package com.xit.biz.ctgy.service;
import com.xit.biz.cmm.entity.CmmFileMst;
import com.xit.biz.ctgy.entity.MinInfoBoard680;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Nonnull;
import java.util.List;
/**
* @author Lim, Jong Uk (minuk926)
* @since 2021-07-16
*/
public interface ICtgyFileService {
MinInfoBoard680 findFiles(Long inCode);
List<MinInfoBoard680> saveFiles(@Nonnull MinInfoBoard680 minInfoBoard680, MultipartFile[] files);
}

@ -0,0 +1,145 @@
package com.xit.biz.ctgy.service.impl;
import com.xit.biz.cmm.entity.CmmFileDtl;
import com.xit.biz.cmm.entity.CmmFileMst;
import com.xit.biz.cmm.entity.ids.CmmFileDtlIds;
import com.xit.biz.cmm.repository.ICmmFileDtlRepository;
import com.xit.biz.cmm.repository.ICmmFileMstRepository;
import com.xit.biz.cmm.service.ICmmFileService;
import com.xit.biz.ctgy.CtgyConstants;
import com.xit.biz.ctgy.entity.MinInfoBoard680;
import com.xit.biz.ctgy.entity.Tf680Recall;
import com.xit.biz.ctgy.repository.IPublicBoardRepository;
import com.xit.biz.ctgy.service.ICtgyFileService;
import com.xit.core.support.jpa.JpaUtil;
import com.xit.core.util.AssertUtils;
import com.xit.core.util.CommUtil;
import com.xit.core.util.DateUtil;
import io.jsonwebtoken.lang.Assert;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Slf4j
@Service
@RequiredArgsConstructor
public class CtgyFileService implements ICtgyFileService {
@Value("${file.cmm.upload.root:c:/data/file/upload}")
private String rootPath;
@Value("${file.cmm.upload.path:/kangnamSIM/simUpFile/}")
private String uploadPath;
@Value("${file.cmm.upload.url}")
private String serviceUrl;
@Value("${file.cmm.upload.allow.ext:}")
private String allowExt;
@Value("${file.cmm.upload.max.size:1024}")
private long maxSize;
private final IPublicBoardRepository repository;
@Override
public MinInfoBoard680 findFiles(Long inCode) {
Assert.notNull(inCode, "대상 게시글[inCode]을 선택해 주세요.");
return repository.findById(inCode).orElse(null);
}
/**
*
* CmmFileMst.fileMstId null, not null
* DB
*
* @param entity MinInfoBoard680
* @param files MultipartFile[]
* @return CmmFileMst
*/
@Override
@Transactional
public List<MinInfoBoard680> saveFiles(@Nonnull MinInfoBoard680 entity, MultipartFile[] files) {
String makePath = "";
makePath = File.separator + DateUtil.getToday("");
// 파일 경로 : upload root 제외
String urlPath = this.uploadPath + makePath;
// 물리적인 파일 저장 위치
String fileUploadPath = this.rootPath + urlPath;
File file = new File(fileUploadPath);
if(!file.exists()) file.mkdirs();
List<MinInfoBoard680> entityList = new ArrayList<>();
for(MultipartFile mf : files){
if(!mf.isEmpty()) {
String orgFileName = "";
try {
orgFileName = StringUtils.cleanPath(Objects.requireNonNull(mf.getOriginalFilename()));
// 파일 저장 && 전송
MinInfoBoard680 savedEntity = repository.findById(entity.getInCode()).orElseGet(MinInfoBoard680::new);
savedEntity.setInFilename(orgFileName);
savedEntity.setInFilesize(mf.getSize());
savedEntity.setInFileurl(serviceUrl+urlPath);
JpaUtil.saveIfNullId(savedEntity.getInCode(), repository, savedEntity);
entityList.add(savedEntity);
mf.transferTo(new File(fileUploadPath + File.separator + orgFileName));
// inputStream을 가져와서
// copyOfLocation (저장위치)로 파일을 쓴다.
// copy의 옵션은 기존에 존재하면 REPLACE(대체한다), 오버라이딩 한다
//Files.copy(multipartFile.getInputStream(), copyOfLocation, StandardCopyOption.REPLACE_EXISTING);
}catch(IOException e){
String errMsg = String.format("File Upload Error :: %s", orgFileName);
//TODO : 에러처리
//return RestError.of(String.format("File Upload Error :: %s", orgFileName));
AssertUtils.isTrue(false, String.format("File Upload Error :: %s", orgFileName));
}
}
}
return entityList;
}
// @Transactional
// public void removeExistsUploadFile(String fileMstId, String orgFileNm){
// CmmFileDtl cmmFileDtl = cmmFileDtlRepository.findByFileMstIdAndOrgFileNmIgnoreCase(fileMstId, orgFileNm);
// if(cmmFileDtl != null){
// CmmFileDtlIds cmmFileDtlIds = new CmmFileDtlIds();
// cmmFileDtlIds.setFileMstId(fileMstId);
// cmmFileDtlIds.setFileId(cmmFileDtl.getFileId());
// cmmFileDtlRepository.deleteById(cmmFileDtlIds);
// new File(this.uploadPath + cmmFileDtl.getFileUpldPath() + File.separator + cmmFileDtl.getFileId()).delete();
// }
// }
}
/*
// File.seperator 는 OS종속적이다.
// Spring에서 제공하는 cleanPath()를 통해서 ../ 내부 점들에 대해서 사용을 억제한다
Path copyOfLocation = Paths.get(uploadDir + File.separator + StringUtils.cleanPath(multipartFile.getOriginalFilename()));
try {
// inputStream을 가져와서
// copyOfLocation (저장위치)로 파일을 쓴다.
// copy의 옵션은 기존에 존재하면 REPLACE(대체한다), 오버라이딩 한다
Files.copy(multipartFile.getInputStream(), copyOfLocation, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
throw new FileStorageException("Could not store file : " + multipartFile.getOriginalFilename());
}
*/

@ -9,6 +9,8 @@ import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.servlet.*; import javax.servlet.*;
@ -36,8 +38,14 @@ public class ReadableRequestWrapperFilter implements Filter {
@Override @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
// Multipart skip
//if(!Objects.equals(request.getContentType(), MediaType.MULTIPART_FORM_DATA_VALUE)) {
ReadableRequestWrapper wrapper = new ReadableRequestWrapper((HttpServletRequest) request); ReadableRequestWrapper wrapper = new ReadableRequestWrapper((HttpServletRequest) request);
chain.doFilter(wrapper, response); chain.doFilter(wrapper, response);
//}else{
// chain.doFilter(request, response);
//}
} }
@Override @Override

@ -4,6 +4,7 @@ import com.xit.core.util.Checks;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebFilter;
@ -13,6 +14,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
@ -25,14 +27,19 @@ import java.util.List;
initParams = {@WebInitParam(name = "extUrl", value = StringUtils.EMPTY)}, initParams = {@WebInitParam(name = "extUrl", value = StringUtils.EMPTY)},
urlPatterns = "/api/*" urlPatterns = "/api/*"
) )
@Order(Ordered.HIGHEST_PRECEDENCE) // Multipart 처리시 우선 순위가 뒤에 와야함
@Order(Ordered.LOWEST_PRECEDENCE)
public class RequestBodyLucyXssFilter implements Filter { public class RequestBodyLucyXssFilter implements Filter {
private List<String> extUrl; private List<String> extUrl;
@Override @Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
// Multipart skip
// if(Objects.equals(req.getContentType(), MediaType.MULTIPART_FORM_DATA_VALUE)) {
// chain.doFilter(req, res);
// return;
// }
HttpServletRequest request = (HttpServletRequest) req; HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; HttpServletResponse response = (HttpServletResponse) res;
RequestLucyXssWrapper reqWrapper = null; RequestLucyXssWrapper reqWrapper = null;

@ -17,8 +17,12 @@ api:
file: file:
cmm: cmm:
upload: upload:
root: c:/data/file/upload
# 공지사항
path: /kangnamSIM/simUpFile
url: http://traffic.gangnam.go.kr
allow: allow:
ext: ext:
max: max:
size: 10000000 size: 10000000
path: d:/data/file/upload

Loading…
Cancel
Save