Spring文書の作成(ファイルを含む)

44864 ワード

  • ファイルも一緒にアップロードされるので別のファイルvoを作成してくれましたか?
  • AttachFilesVO
    package kr.or.ddit.cus.vo;
    
    import java.util.Date;
    
    import org.springframework.format.annotation.DateTimeFormat;
    
    public class AttachFilesVO {
    	private String id;
    	private String uploadFileName;
    	private String uploadFileSize;
    	private int seq;
    	@DateTimeFormat(pattern ="yyyy-MM-dd")
    	private Date registDt;
    	@DateTimeFormat(pattern ="yyyy-MM-dd")
    	private Date updateDt;
    	private String registerId;
    	private String updaterId;
    	
        	// getter,setter, toString 생략
    	
    }
    
    CusVO
    package kr.or.ddit.cus.vo;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.hibernate.validator.constraints.NotBlank;
    import org.springframework.web.multipart.MultipartFile;
    
    public class CusVO {
    	
    	// 고객번호 CUS00001
    	private String cusNum;
    	@NotBlank
    	private String cusNm;
    	@NotBlank
    	private String pne;
    	@NotBlank
    	private String addr;
    	
    	private String addr1;
    	private String addr2;
    	
    	// 순번 추가 부탁
    	private int rnum;
    	
    	// 다중 파일 객체
    	private MultipartFile[] uploadFile;
    	
    	// 다중 파일 객체의 파일명
    	private String uploadFileName;
    	
    	// 다중 파일 업로드 객체
    	private List<AttachFilesVO> attachFilesVO;
    	
    	
    }
    
    mapper.xml
    <!-- * selectKey?
    	일련번호 처리
    	마이바티스는 쿼리 실행 시 파라미터를 치환해줌 
    	 -->
    	 <!-- 고객 등록 -->
    	<insert id="insert" parameterType="cusVO">
    		<!-- ******* -->
    		<selectKey order="BEFORE" keyProperty="cusNum" resultType="String">
    			SELECT 'CUS' || LPAD(NVL(MAX(SUBSTR(CUS_NUM,4)),0)+1,5,'0') FROM CUS
    		</selectKey>
    		INSERT INTO CUS(CUS_NUM,CUS_NM,ADDR,PNE)
    		VALUES(#{cusNum},#{cusNm},#{addr}|| ' ' || #{addr1} || ' ' || #{addr2}, #{pne})
    	</insert>
    	
    	<!-- attach_files 테이블로 첨부파일 insert -->
    	<insert id="insertAttachFiles" parameterType="attachFilesVO">
    		<foreach collection="list" item="item" index="index" 
    			open="INSERT ALL " separator=" " close="SELECT * FROM DUAL">
    			INTO ATTACH_FILES(ID,SEQ,UPLOAD_FILE_NAME,UPLOAD_FILE_SIZE,REGIST_DT,REGISTER_ID)
    			VALUES(#{item.id},#{item.seq},#{item.uploadFileName},#{item.uploadFileSize},SYSDATE,#{item.registerId})
    		</foreach>
    	</insert>
    mapper
    public interface CusMapper {
    public int insert(CusVO cusVO);
    	
    public int insertAttachFiles(List<AttachFilesVO> attachFilesVO);
    }
    serviceImpl
    package kr.or.ddit.cus.service.impl;
    
    @Service
    public class CusServiceImpl implements CusService {
    	@Autowired
    	CusMapper cusMapper;
    	
    	private static final Logger logger = 
    			LoggerFactory.getLogger(CusServiceImpl.class);
    	
    	//고객 등록
    	@Override
    	public int insert(CusVO cusVO) {
    		//cus 테이블로 insert
    		int result = cusMapper.insert(cusVO);
    		
    		//업로드한 파일 객체들
    		MultipartFile[] uploadFile = cusVO.getUploadFile();
    		
    		//파일 저장 경로 설정
    		String uploadFolder = "D:\\A_TeachingMaterial\\6.JspSpring\\workspace\\dasuriProj\\src\\main\\webapp\\resources\\upload";
    		
    		//연/월/일 폴더 생성 시작-------
    		File uploadPath = new File(uploadFolder, getFolder());
    		logger.info("uploadPath : " + uploadPath);
    		
    		if(uploadPath.exists()==false) {//해당 경로가 없으면 생성해줘야함
    			uploadPath.mkdirs();			
    		}
    		//연/월/일 폴더 생성 끝-------
    		
    		//업로드한 파일 객체들의 파일명과 크기 정보를 넣은 후 insert 할 리스트 객체
    		List<AttachFilesVO> attachFilesVO = new ArrayList<AttachFilesVO>();
    		
    		int seq = 1;
    		
    		//이미지 3개를 업로드 한다면 3회 반복
    		for(MultipartFile multipartFile : uploadFile) {
    			logger.info("-----------");
    			logger.info("파일명 : " + multipartFile.getOriginalFilename());
    			logger.info("파일크기 : " + multipartFile.getSize());
    			
    			//각 파일 별로 세팅할 VO 
    			AttachFilesVO vo = new AttachFilesVO();
    			//1) 파일id(기본키데이터), 파일시퀀스번호,파일명과 크기를 세팅
    			vo.setId(cusVO.getCusNum());	//고정값
    			vo.setSeq(seq++);
    			vo.setUploadFileSize(""+multipartFile.getSize());
    			vo.setRegisterId("admin");//로그인 한 아디디로 교체해야 함
    			//-----------UUID 파일명 처리 시작 ----------------------------
    			//동일한 이름으로 업로드되면 기존 파일을 지우게 되므로 이를 방지하기 위함
    			UUID uuid = UUID.randomUUID();
    			
    			String uploadFileName = uuid.toString() + "-" + multipartFile.getOriginalFilename();
    			// c:\\upload\\gongu03.jpg으로 조립
    			// 이렇게 업로드 하겠다라고 설계 uploadFolder -> uploadPath
    			// /resources/upload/2022/02/21/asdfsadfsdafsda_test.jpg
    			vo.setUploadFileName("/resources/upload/" + getFolder() + "/" + uploadFileName);
    			
    			File saveFile = new File(uploadPath,uploadFileName);
    			//-----------UUID 파일명 처리 끝 ----------------------------
    			
    			try {
    				//transferTo() : 물리적으로 파일 업로드가 됨
    				multipartFile.transferTo(saveFile);
    			
    				//-------썸네일 처리 시작---------
    				//이미지 파일인지 체킹
    				if(checkImageType(saveFile)) {
    					logger.info("이미지 파일? true");
    					//uploadPath : 연/월/일이 포함된 경로
    					//uploadFileName : UUID가 포함된 파일명
    					FileOutputStream thumbnail = 
    							new FileOutputStream(
    									new File(uploadPath,"s_"+uploadFileName));
    					Thumbnailator.createThumbnail(multipartFile.getInputStream(),
    							thumbnail, 100, 100);
    					thumbnail.close();
    				}else {
    					logger.info("이미지 파일? false");
    				}
    				//-------썸네일 처리 끝---------
    				
    				//파일 실제 명을 list에 담음
    				attachFilesVO.add(vo);
    			}catch(Exception e){
    				logger.info(e.getMessage());
    			}//end catch
    		}
    		
    		
    		//attach_files 테이블로 insert
    		 int filesResult = cusMapper.insertAttachFiles(attachFilesVO);
    		
    		return result;
    	}
    	
    	//첨부파일을 보관하는 폴더를 연/월/일 계층 형태로 생성하기 위함
    	private String getFolder() {
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    		Date date = new Date();
    		String str = sdf.format(date);
    		return str.replace("-", "/");
    	}
    	
    	//특정한 파일이 이미지 타입인지 검사해주는 메소드
    	private boolean checkImageType(File file) {
    		try {
    			//file.toPath() : 파일의 전체 경로
    			logger.info("file.toPath() : " + file.toPath());
    			String contentType = Files.probeContentType(file.toPath());
    			logger.info("contentType : " + contentType);
    			//contentType이 image로 시작하면 이미지 타입이므로 true를 리턴함
    			return contentType.startsWith("image");
    		}catch(IOException e) {
    			e.printStackTrace();
    		}
    		return false;
    	}
    }
    
    controller
    package kr.or.ddit.cus.controller;
    
    @RequestMapping("/cus")
    @Controller
    public class CusController {
    	private static final Logger logger = 
    			LoggerFactory.getLogger(CusController.class);
    	
    	@Autowired
    	CusService cusService;
    	
    	
    	
    	@GetMapping("/insert")
    	public String insert(Model model) {
    		Map<String,String> pageHeader = new HashMap<String, String>();
    		pageHeader.put("subtitle", "Customer");
    		pageHeader.put("title", "고객 등록");
    		
    		model.addAttribute("pageHeader", pageHeader);
    		model.addAttribute("cusVO", new CusVO());
    		//forwarding
    		return "cus/insert";
    	}
    	
    	
    	// form의 입력값들이 CusVO에 매핑됨 메모리에 올라와있는 cusVO의 검증결과가 BindingResult에 들어감
    	@PostMapping("/insert")
    	public String insertPost(@Validated CusVO cusVO, BindingResult result) {
    		
    		// 검증 오류 발생시
    		if(result.hasErrors()) {
    			
    			List<ObjectError> allErrors = result.getAllErrors();
    			List<ObjectError> globalErrors = result.getGlobalErrors();
    			List<FieldError> fieldErrors = result.getFieldErrors();
    			//validation 중에 어떤 오류가 나왔는지 확인..
    			for(int i=0;i<allErrors.size();i++) {
    				ObjectError objectError = allErrors.get(i);
    				logger.info("objectError : " + objectError);
    			}
    			
    			for(ObjectError objectError : globalErrors) {
    				logger.info("objectError : " + objectError);
    			}
    			
    			for(FieldError fieldError : fieldErrors) {
    				logger.info("fieldError : " + fieldError.getDefaultMessage());
    			}
    			
    			// redirect(x) => 데이터를 보낼 수 없음
    			// forwarding
    			return "cus/insert";
    		}
    		
    		// insert 처리
    		logger.info("cusVO: " + cusVO.toString());
    		
    		int insertResult = cusService.insert(cusVO);
    		
    		if(insertResult>0) { //고객 등록 성공
    		
    			// 목록으로 이동
    			return "redirect:/cus/list";
    			
    		}else {
    			return "cus/insert";
    		}
    		
    	}
    
    
    VIEW(jsp)
    <%@ page language="java" contentType="text/html; charset=UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    <script type="text/javascript" src="/resources/js/jquery-3.6.0.js"></script>
    <form:form modelAttribute="cusVO" method="post" action="/cus/insert" enctype="multipart/form-data">  
    <ul class="list-group list-group-flush">
      <li class="list-group-item p-3">
        <div class="row">
          <div class="col-sm-12 col-md-6">        
              <strong class="text-muted d-block mb-2">고객 이름</strong>
              <div class="form-group">
                <form:input path="cusNm" class="form-control" />
                <font color="red" style="font-size:8pt;">
                	<form:errors path="cusNm" />
                </font>
              </div>
              <strong class="text-muted d-block mb-2">연락처</strong>
              <div class="form-group">
                <form:input path="pne" class="form-control" />
                <font color="red" style="font-size:8pt;">
                	<form:errors path="pne" />
                </font> 
              </div>
              <strong class="text-muted d-block mb-2">고객 주소</strong>
              <div class="form-row">
    	          <div class="form-group col-md-7">
    	            <form:input path="addr" class="form-control" />
    	            <font color="red" style="font-size:8pt;">
    	            	<form:errors path="addr" />
    	            </font> 
    	          </div>
    	          <div class="form-group col-md-5">
    	            	<button type="button" class="mb-2 btn btn-sm btn-info mr-1"
    	            	onclick="openHomeSearch()">우편번호 검색</button>
    	          </div>
              </div>
              <div class="form-group">
                <input type="text" class="form-control" name="addr1" id="addr1" placeholder="" />
              </div>
              <div class="form-group">
                <input type="text" class="form-control" name="addr2" id="addr2" placeholder="" />
              </div>
              <div class="form-group" style="float:right;">
    			<button type="submit" class="mb-2 btn btn-sm btn-success mr-1">등록</button>
    			<button type="reset" class="mb-2 btn btn-sm btn-danger mr-1">취소</button>
    		  </div>
          </div>
          <div class="col-sm-12 col-md-6">
            <strong class="text-muted d-block mb-2">고객 이미지</strong>
            <div class="form-group">
            	<div class="imgs_wrap"></div>
            </div>
            <div class="form-group">
            	<input type="file" id="input_imgs" name="uploadFile" multiple />
            </div>
          </div>
        </div>
      </li>
    </ul>
    </form:form>
    <script type="text/javascript">
    	$(function(){
    		$("#input_imgs").on("change",handleImgsFilesSelect);
    	});
    	//e : change이벤트를 받음
    	function handleImgsFilesSelect(e){
    		//이벤트가 일어난 파일객체의 이미지 파일들을 가져옴
    		var files = e.target.files;
    		//파일들을 배열로 만들어 관리
    		var filesArr = Array.prototype.slice.call(files);
    		//f : 각각의 파일 객체
    		filesArr.forEach(function(f){
    			if(!f.type.match("image.*")){
    				alert("이미지만 가능합니다.");
    				//업로드 종료(실패)
    				return;
    			}
    			//각 이미지를 reader로 읽어들임
    			var reader = new FileReader();
    			reader.onload = function(e){
    				var img_html = "<img src=\"" + e.target.result + "\" style='width:100px;' />";
    				$(".imgs_wrap").append(img_html);
    			}
    			reader.readAsDataURL(f);
    		});//end forEach
    	}
    </script>