본문 바로가기
개발자/자바 & 스프링

스프링 마이바티스 자바 파일 업로드 / Spring - mybatis Java file upload

by mansfield 2023. 2. 14.
반응형

파일 VO

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileVO {
	
	private int fileNo;
	private int boardNo;
	private String filename;
	private String filepath;
	
}

Board VO

import java.util.ArrayList;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Board {
	
	private int boardNo;
	private String boardTitle;
	private String boardContent;
	private String boardWriter;
	private String boardDate;
	
	private ArrayList<FileVO> fileList;	//DB에 전달하기 위해 Board VO 내에 생성
	
}

파일업로드 JSP 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>게시글작성(파일추가)</h1>
	<hr>
	<form action="/boardWrite2.do" method="post" enctype="multipart/form-data">
		<fieldset>
			제목 : <input type="text" name="boardTitle"><br>
			첨부파일 : <input type="file" name="upfile" multiple><br>
			내용 : <textarea rows="20" cols="50" name="boardContent"></textarea><br>
			<input type="hidden" name="boardWriter" value="${sessionScope.m.memberId }">
			<input type="submit" value="작성하기"> 
		
		</fieldset>
	</form>	
</body>
</html>

파일 업로드를 위해 폼태그를 작성할때는 form에 method="post" , enctype="multipart/form-data" 를 반드시 적용해 주어야 한다. 


파일 업로드 Controller 

@RequestMapping("/boardWrite2.do")
	public String boardWrite2(Board b, MultipartFile[] upfile, HttpServletRequest request) {
		//파일 목록을 저장할 리스트 생성
		ArrayList<FileVO> fileList = new ArrayList<FileVO>();
		
		//MultipartFile 배열을 첨부파일의 갯수만큼 길이가 생성(단, 첨부파일이 없어도 길이는 무조건1)
		//첨부파일이 없는 경우는 배열의 첫번째 파일이 비어있는지 체크하는 방식
		if(upfile[0].isEmpty()) {
			//사용하지 않지만 보여주기 위해 표현
			//첨부파일이 없는 경우 수정할 로직이 없음
		}else {
			
			//첨부파일이 있는 경우 파일업로드 작업 진행
			//1. 파일업로드 경로 설정 (HttpServletRequest 객체를 이용해서 경로를 구해옴)
			//   request.getSession().getServletContext() -> /webapp/ 까지의 경로
			//   .getRealPath("/resource/upload/board/") >> 추가 파일 경로
			String savePath 
				= request.getSession().getServletContext().getRealPath("/resources/upload/board/");
			
			//2. 반복문을 이용한 파일 업로드 처리
			for(MultipartFile file : upfile) {
				//파일명이 기존 파일과 겹치는 경우 기존파일을 삭제하고 새 파일만 남는 현상이 생김(덮어쓰기)
				//파일명 중복인 경우 처리해줘야함
				//사용자가 업로드한 파일 이름
				String filename = file.getOriginalFilename();
				
				//test.txt -> text_1.txt 
				//업로드한 파일명이 test.txt인 경우 -> text 와 .txt로 분리 
				//subString(포함 시작,미포함 끝)
				//subString(미포함시작~끝까지)
				String onlyFilename = filename.substring(0, filename.lastIndexOf(".")); //test
				String extention = filename.substring(filename.lastIndexOf("."));		//.txt	
				
				//실제 업로드할 파일명 저장할 변수
				String filepath = null;
				
				//파일명 중복 시 뒤에 붙일 숫자 변수 
				int count = 0;
				
				while(true) {
					if(count == 0) {
						//반복 첫번째 회차에는 원본 파일명을 그대로 적용
						filepath = onlyFilename + extention; //test.txt
					}else {
						filepath = onlyFilename + "_" + count + extention;
					}
					//java.io 의 File
					//파일 경로 + 파일이름으로 체크할 파일 생성
					File checkFile = new File(savePath + filepath);
					
					//체크파일명이 해당 경로에 존재하지 않으면 while문 break 
					if(!checkFile.exists()) {
						break;
					}
					//파일명이 겹치지 않으면 count를 1씩 올려 다시 while문 실행
					count++;
				}//while문 end
				
				//파일명 중복 검사가 끝난 시점 > 해당 파일 업로드
				try {
					//중복 처리가 끝난 파일명(filepath)으로 파일을 업로드할 FileOutputStream 객체 생성
					FileOutputStream fos = new FileOutputStream(new File(savePath+filepath));
					
					//업로드 속도 증가를 위한 보조스트림 생성
					BufferedOutputStream bos = new BufferedOutputStream(fos);
					
					//파일 업로드
					byte[] bytes = file.getBytes();
					bos.write(bytes);
					bos.close();
					
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//서버 파일업로드 작업 끝(파일 1개 단위)
				
				//DB에 파일 정보 저장
				FileVO fileVO = new FileVO();
				fileVO.setFilename(filename);	//업로드한 실제 파일 이름
				fileVO.setFilepath(filepath);	//서버에 저장될 이름
				fileList.add(fileVO);
				
				
			}//for문 end
			
		}//else end
		
		int result = service.insertBoard(b,fileList);
		//성공 실패 결과 비교, fileList.size() == result 인 경우 insert 성공
			
		return "redirect:/boardList.do";
	}

파일 업로드 Service

	public int insertBoard(Board b, ArrayList<FileVO> fileList) {
		
		//board테이블에 게시글 작성
		int result1 = dao.insertBoard(b);
		int result = 0;
		
		if(result1>0) {
			//방금 작성한 게시글이 가장 최근에 작성한 게시글이기 때문에 boardNo값이 가장 큼 따라서 쿼리문은 max(board_no)로 작성
			int boardNo = dao.selectBoardNo();

			for(FileVO file : fileList) {
				//mybatis에서 selectkey를 사용할 경우 b객체에 boardNo값이 담겨온다
				//file.setBoardNo(b.getBoardNo());
				file.setBoardNo(boardNo);
				result += dao.insertFile(file);
			}//for문 end
			
		}else {
			result = -1;
		}//if문 end
		
		return result;
		
		
	}

게시글과 파일을 각각 데이터베이스에 따로 넣어주어야 함. 


파일 업로드 mybatis DB 쿼리 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="boardDao">

  <insert id="insertBoard" parameterType="board">
  	insert into board 
  	values(board_seq.nextval, #{boardTitle}, #{boardContent}, #{boardWriter}, to_char(sysdate, 'yyyy-mm-dd'))
  	
  	<!-- insert를 하고 난 후(order="AFTER")의 결과를 boardNo(keyProperty="boardNo", parameterType="board" / 
  											return이 아니라 객체)에 넣어주고 아래의 sql문 실행-->
  	<selectKey resultType="int" order="AFTER" keyProperty="boardNo">
  		select max(board_no) from board
  	</selectKey>
  </insert>
  
  <select id="selectBoardNo" resultType="int">
  	select max(board_no) from board
  </select>
  
  <insert id="insertFile" parameterType="file">
  	insert into file_tbl values(file_seq.nextval, #{boardNo}, #{filename}, #{filepath}) 
  </insert>

</mapper>

 


파일 다중 업로드시

@RequestMapping("admin_oneQuestion_replyOk.do")
	public void admin_oneQuestionReplyOk(ServiceDTO serviceDto, 
			@RequestParam("page") int nowPage, HttpServletResponse response ,
			@RequestParam("upfile")MultipartFile[] upfile) throws IOException {
		System.out.println(serviceDto);
		System.out.println(serviceDto.getService_group());
		
		//service_num의 max값을 구해오는 메소드
		//num = 원글의 group과 일치
		int num = this.dao.getMaxServiceNum();
				
		//group으로 step과 index의 max값을 가져옴
		int step = this.dao.getStep(serviceDto.getService_group());
		int indent = this.dao.getIndent(serviceDto.getService_group());
		
		serviceDto.setService_indent(indent);
		serviceDto.setService_step(step);
		serviceDto.setService_num(num);
		
		//답변등록
		int check = this.dao.addOneQuestionReply(serviceDto);
		
		//답변을 등록했으면 원글의 상태를 '답변완료'로 바꿔준다
		this.dao.updateOneQuetionOrgingSi(num);
		
		// fileDTO에 service_num의 fk 값을 주기위해 조회한다.
		int sNum = this.dao.getServiceNumValue();
		
		
		System.out.println("upfile.length///"+upfile.length);		
		//MultipartFile[] : 첨부파일의 갯수만큼 길이가 생성 (단, 첨부파일이 없어도 길이는 무조건 1)
		//input이기 때문에 일단 데이터가 넘어오기 때문
		//첨부파일이 없는 경우는 배열의 첫번째 파일이 비어있는지 체크하는 방식		
		
		
		//첨부파일이 있는 경우 파일 업로드 작업 진행
		//1. 파일업로드 경로 설정(HttpServletRequest 객체를 이용해서 경로를 가져옴)
		//request.getSession().getServletContext().getRealPath()  ->  /webapp/폴더경로
		String savePath 
		= "C:\\Users\\ubg11\\ ~~~ \\src\\main\\webapp\\resources\\image_service\\upload\\";
		
		//request.getSession().getServletContext().getRealPath("/resources/image_service/upload/");
		//패스 경로 위에처럼 해야하는데 안돼서 직접 복사
		
		
		//2. 반복문을 이용한 파일 업로드 처리
		for(MultipartFile file : upfile) {
			//넘어온 파일이 비어있을 경우 for문을 continue함 (실행하지 않고 빠져나가 다음 것을 실행함)
			if(file ==null || file.isEmpty()) {continue;}
			
			//파일명이 기존 파일과 겹치는 경우 기존 파일을 삭제하고 새 파일만 남는 현상이 생김(덮어쓰기)
			//파일명 중복처리 (겹치면 넘버링 처리)
			//사용자가 업로드한 파일 이름(원본파일이름) 
			String file_name = file.getOriginalFilename();
			
			//test.txt 중복일 경우 -> test_1.txt -> test_2.txt
			//업로드한 파일명이 test.txt인 경우 -> (test) (.txt) 두 부분으로 분리 (이름과 확장자로 분리)
			//file_name.lastIndexOf(".") : file_name 중 0번째 자리부터 뒤에서 가장 가까운 인덱스 "." 까지 찾아 자른다.
			String onlyFilename = file_name.substring(0, file_name.lastIndexOf(".")); // test
			String extention = file_name.substring(file_name.lastIndexOf(".")); // .txt
			
			//실제 업로드할 파일명을 저장할 변수
			String file_path = null;
			
			//파일명 중복시 뒤에 붙일 수자 변수
			int count = 0;
			
			//중복이 안될때까지 반복해야하기 때문에 무한반복 사용
			while(true) {
				
				if(count == 0) {
					//반복 첫째회차에는 원본 파일명 그대로 적용
					file_path = onlyFilename + extention; //test.txt
				}else {
					file_path = onlyFilename + "_" + count + extention; //test + _  + count값 + .txt
				}
				
				//저장경로에 들어가 파일과 같은 이름이 있는지 확인
				File checkFile = new File(savePath+file_path);
				if(!checkFile.exists()) {	//.exists() : 존재여부 확인 
					//중복 파일명이 존재하지 않을때까지 반복후 존재하지 않으면 break로 while문을 나온다.
					break; 
				}
				count++;
				
			}//파일명 중복 검사가 끝난 시점 -> 해당파일 업로드 작업
			
			try {
				//중복처리가 끝난 파일명(file_path)으로 파일을 업로드할 FileOutputStream 객체 생성
				FileOutputStream fos = new FileOutputStream(new File(savePath+file_path));
				//업로드 속도증가를 위한 보조 스트림 생성
				BufferedOutputStream bos = new BufferedOutputStream(fos);
				//파일업로드 처리
				byte[] bytes = file.getBytes();
				bos.write(bytes);
				bos.close();
			} catch (Exception e) {
			
				// TODO: handle exception
				e.printStackTrace();
			} 
			//서버 파일 업로드 작업 끝(파일 1개 단위)
			ServiceFileDTO fDto = new ServiceFileDTO();
			fDto.setService_num(sNum);
			fDto.setFile_name(file_name);
			fDto.setFile_path(file_path);
			//fileList.add(fDto);
			
			//반복적으로 서버에 추가
			this.dao.addFile(fDto);
		}
		
		response.setContentType("text/html; charset=UTF-8");
		
		PrintWriter out = response.getWriter();
		
		if (check > 0) {

			out.println("<script>");
			out.println("alert('작성 완료')");
			out.println("location.href='oneQuestion_list.do?page=" + nowPage + "'");
			out.println("</script>");

		} else {

			out.println("<script>");
			out.println("alert('작성 실패')");
			out.println("history.back()");
			out.println("</script>");
		}
		
	}

 

300x250

댓글