Spring

12.16.(금) Spring Framework(29): 자료실 기능(3)

콜라든포비 2022. 12. 18. 00:04

업로드 cont.

이전 시간에 dataPost에 있는 form을 submit까지 했다.

이제 dataPostOk로 url생성이 된 form을 컨트롤러에서 받아보자.

여러명의 클라이언트가 업로드하는 파일을 한 폴더(upload)에 저장할건데,

이름이 겹칠 수도 있기 때문에 파일은 submit받을때 고유의 이름으로 rename하는 과정을 거칠 것이다.

컨트롤러 매핑

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
	// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
	ModelAndView mav = new ModelAndView();
	
	return mav;
}

HttpServleRequest로 form의 값과 파일객체를 서버로 가져오고, 세션으로부터 로그인된 클라이언트의 정보를 가져올 수 있다.

파일 업로드를 실행하면, upload폴더에 저장된다고 했는데, 이 위치는 우리가 볼 수 있는 워크스페이스 폴더에 있는 upload폴더가 아니다. 정확한 위치는,

/Users/poby/StudySpring/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/webSpringTest/upload

여기에 저장되므로, 한쪽에 열어놓고 작업을 진행하자.

우리가 볼 수 있는 위치의 webSpringTest폴더는 편집할때 필요한 파일들을 모아놓은 폴더이다.

서버가 실행되면 .metadata폴더에 있는 곳에 저장된다. 업로드되는 저 위치의 경로는 getRealPath()메소드로 구할 수 있다.

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
	// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
	ModelAndView mav = new ModelAndView();
	
	// 작성자
	vo.setUsername((String)req.getSession().getAttribute("logUsername"));
	
	// 파일 업로드 경로
	String path = req.getSession().getServletContext().getRealPath("/upload");
	System.out.println("path -> " + path);
	
	return mav;
}

다음으로 req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다.

MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다.

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
	// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
	ModelAndView mav = new ModelAndView();
	
	// 작성자
	vo.setUsername((String)req.getSession().getAttribute("logUsername"));
	
	// 파일 업로드 경로
	String path = req.getSession().getServletContext().getRealPath("/upload");
	
	// req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다
	// MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다
	MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
	
	return mav;
}

mr에서 MultipartFile객체를 구해온다.

1개 이상의 파일이 구해질 수 있기 때문에 List컬렉션으로 담아줬다.

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
	// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
	ModelAndView mav = new ModelAndView();
	
	// 작성자
	vo.setUsername((String)req.getSession().getAttribute("logUsername"));
	
	// 파일 업로드 경로
	String path = req.getSession().getServletContext().getRealPath("/upload");
	
	// req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다
	// MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다
	MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
	
	// mr에서 MultipartFile객체를 구해온다
	List<MultipartFile> files = mr.getFiles("filename");
	
	return mav;
}

getFiles()의 매개변수로는 form에서 만들었던 file타입의 input태그의 name을 넣었다.

<li>
	<input type="file" name="filename" id="filename1"/><br>
	<input type="file" name="filename" id="filename2"/>
</li>

첨부한 파일이 존재할때 실행되도록 조건문을 붙여줘야한다.

모든 파일에 대해서 List에서 get()으로 하나씩 객체를 생성하여 원래파일명을 문자열로 구한다.

그런 후 File객체에 이전에 구한 path와 방금 구한 원래파일명을 매개변수로 넣어서 생성한다.

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
    // HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
    ModelAndView mav = new ModelAndView();

    // 작성자
    vo.setUsername((String)req.getSession().getAttribute("logUsername"));

    // 파일 업로드
    String path = req.getSession().getServletContext().getRealPath("/upload");

    // req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다
    // MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다
    MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;

    // mr에서 MultipartFile객체를 구해온다
    List<MultipartFile> files = mr.getFiles("filename");

    // 첨부한 파일이 있을 때---------------------------------------------
    if(files!=null) {
        // 첨부파일의 개수만큼 반복한다************************************
        for(int i=0; i<files.size(); i++) {
            MultipartFile mf = files.get(i);	// List에서 파일얻어오기
            String filename = mf.getOriginalFilename();	// 업로드 시 선택한 원래파일명 얻어오기

            // 서버에 file이 없으면 그대로 업로드하고, 있으면 새로운 파일명 생성한다
            if(filename!=null && !filename.equals("")) {	// 원래 파일명이 존재하면
                File f = new File(path, filename);
            }
        }
        // *********************************************************
    }


    //---------------------------------------------------------

    return mav;
}

그렇게 생성한 파일과 같은 이름이 존재하면, 원래 파일명 끝에 (1)을 붙여서 수정하는 기능을 만들어보자.

반복문을 생성해서 같은 이름이 존재하지 않을때까지 번호를 붙여보자.

파일 이름에는 맨 마지막 . 을 기준으로 확장자명이 정해지기 때문에, 파일명과 확장자명을 맨 마지막 .을 기준으로 분리시켰다.

분리시킨 파일명과 확장자명 사이에 (i)를 넣어서 새로운 파일명을 만든 다음에, 그 이름으로 이전에 만든 File객체를 재정의한다.

재정의된(번호를 붙인) File객체가 서버에 존재하지 않을때, 새로 번호를 붙인 이름을 원래파일명(저장시킬 파일명)에 정의하고, 반복문을 종료시킨다.

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
	// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
	ModelAndView mav = new ModelAndView();
	
	// 작성자
	vo.setUsername((String)req.getSession().getAttribute("logUsername"));
	
	// 파일 업로드
	String path = req.getSession().getServletContext().getRealPath("/upload");
	
	// req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다
	// MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다
	MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
	
	// mr에서 MultipartFile객체를 구해온다
	List<MultipartFile> fileList = mr.getFiles("filename");
	
	// 첨부한 파일이 있을 때---------------------------------------------
	if(fileList!=null) {
		// 첨부파일의 개수만큼 반복한다************************************
		for(int i=0; i<fileList.size(); i++) {
			MultipartFile mf = fileList.get(i);	// List에서 파일얻어오기
			String file = mf.getOriginalFilename();	// 업로드 시 선택한 원래파일명 얻어오기
			
			// 서버에 file이 없으면 그대로 업로드하고, 있으면 새로운 파일명 생성한다
			if(file!=null && !file.equals("")) {	// 원래 파일명이 존재하면
				File f = new File(path, file);
				if(f.exists()) {	// 파일이 존재하는지 알려준다 -> true:있다, false:없다
					// 같은 이름의 파일이 있음 -> 파일명 변경 : file(1).txt
					for(int renameNum=1;;renameNum++) {
						int dot = file.lastIndexOf(".");	// 파일명, 확장자 분리
						String filename = file.substring(0, dot);	// 파일명
						String extension = file.substring(dot+1);	// 확장자
						
						// 새로운 파일명
						String newFilename = filename+"("+renameNum+")."+extension;
						f = new File(path, newFilename);
						
						// 기존 파일 중 새로운 파일명과 같은 파일이 없으면 반복문 중단
						if(!f.exists()) {
							file = newFilename;
							break;
						}
					}
					
				}
				// 업로드하기
				try {
					mf.transferTo(new File(path, file));
				}catch(IOException e) {
					System.out.println("파일 업로드 예외발생");
					e.getMessage();
				}
			}
			
		}
		// *********************************************************
	}
	//---------------------------------------------------------

	return mav;
}

업로드는 MultipartFile객체의 transferTo()메소드를 이용해서 할 수 있다.

IOException이 발생하기 때문에 try~catch문으로 감싸준다.

 

마지막으로 업로드한 파일명을 보관할 변수를 만들어준다. 파일이 두 개 이상일 수 있기 때문에 List컬렉션으로 만들어주었다.

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
	// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
	ModelAndView mav = new ModelAndView();
	
	// 작성자
	vo.setUsername((String)req.getSession().getAttribute("logUsername"));
	
	// 파일 업로드
	String path = req.getSession().getServletContext().getRealPath("/upload");
	
	// req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다
	// MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다
	MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
	
	// mr에서 MultipartFile객체를 구해온다
	List<MultipartFile> fileList = mr.getFiles("filename");
	
	// 첨부한 파일이 있을 때---------------------------------------------
	// 업로드한 파일명을 보관할 변수
	List<String> filenameList = new ArrayList<String>();
	if(fileList!=null) {
		// 첨부파일의 개수만큼 반복한다************************************
		for(int i=0; i<fileList.size(); i++) {
			MultipartFile mf = fileList.get(i);	// List에서 파일얻어오기
			String file = mf.getOriginalFilename();	// 업로드 시 선택한 원래파일명 얻어오기
			
			// 서버에 file이 없으면 그대로 업로드하고, 있으면 새로운 파일명 생성한다
			if(file!=null && !file.equals("")) {	// 원래 파일명이 존재하면
				File f = new File(path, file);
				if(f.exists()) {	// 파일이 존재하는지 알려준다 -> true:있다, false:없다
					// 같은 이름의 파일이 있음 -> 파일명 변경 : file(1).txt
					for(int renameNum=1;;renameNum++) {
						int dot = file.lastIndexOf(".");	// 파일명, 확장자 분리
						String filename = file.substring(0, dot);	// 파일명
						String extension = file.substring(dot+1);	// 확장자
						
						// 새로운 파일명
						String newFilename = filename+"("+renameNum+")."+extension;
						f = new File(path, newFilename);
						
						// 기존 파일 중 새로운 파일명과 같은 파일이 없으면 반복문 중단
						if(!f.exists()) {
							file = newFilename;
							break;
						}
					}
					
				}
				// 업로드하기
				try {
					mf.transferTo(new File(path, file));
				}catch(IOException e) {
					System.out.println("파일 업로드 예외발생");
					e.getMessage();
				}
				// 업로드한 파일명(원래파일명 or 새로운 파일명)
				filenameList.add(file);
			}
			
		}
		// *********************************************************
	}
	
	
	//---------------------------------------------------------
	
	return mav;
}

위 List에 담긴 업로드된 파일명을 차례로 VO의 filename1과 filename2에 set해준다.

제대로 들어갔는지 확인하기 위해 콘솔에 찍어준다.

// 자료 올리기 페이지
	@PostMapping("/data/dataPostOk")
	public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
		// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
		ModelAndView mav = new ModelAndView();
		
		// 작성자
		vo.setUsername((String)req.getSession().getAttribute("logUsername"));
		
		// 파일 업로드
		String path = req.getSession().getServletContext().getRealPath("/upload");
		
		// req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다
		// MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다
		MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
		
		// mr에서 MultipartFile객체를 구해온다
		List<MultipartFile> fileList = mr.getFiles("filename");
		
		// 첨부한 파일이 있을 때---------------------------------------------
		// 업로드한 파일명을 보관할 변수
		List<String> filenameList = new ArrayList<String>();
		if(fileList!=null) {
			// 첨부파일의 개수만큼 반복한다************************************
			for(int i=0; i<fileList.size(); i++) {
				MultipartFile mf = fileList.get(i);	// List에서 파일얻어오기
				String file = mf.getOriginalFilename();	// 업로드 시 선택한 원래파일명 얻어오기
				
				// 서버에 filename이 없으면 그대로 업로드하고, 있으면 새로운 파일명 생성한다
				if(file!=null && !file.equals("")) {	// 원래 파일명이 존재하면
					File f = new File(path, file);
					if(f.exists()) {	// 파일이 존재하는지 알려준다 -> true:있다, false:없다
						// 같은 이름의 파일이 있음 -> 파일명 변경 : file(1).txt
						for(int renameNum=1;;renameNum++) {
							int dot = file.lastIndexOf(".");	// 파일명, 확장자 분리
							String filename = file.substring(0, dot);	// 파일명
							String extension = file.substring(dot+1);	// 확장자
							
							// 새로운 파일명
							String newFilename = filename+"("+renameNum+")."+extension;
							f = new File(path, newFilename);
							
							// 기존 파일 중 새로운 파일명과 같은 파일이 없으면 반복문 중단
							if(!f.exists()) {
								file = newFilename;
								break;
							}
						}
						
					}
					// 업로드하기
					try {
						mf.transferTo(new File(path, file));
					}catch(IOException e) {
						System.out.println("파일 업로드 예외발생");
						e.getMessage();
					}
					// 업로드한 파일명(원래파일명 or 새로운 파일명)
					filenameList.add(file);
				}
				
			}// *********************************************************
			
			// 업로드한 파일명을 vo의 filename1, filename2에 set한다
			vo.setFilename1(filenameList.get(0));
			if(filenameList.size()==2) vo.setFilename2(filenameList.get(1));
		}//---------------------------------------------------------
		System.out.println("f1 ->" + vo.getFilename1());
		System.out.println("f2 ->" + vo.getFilename2());

		return mav;
	}

여기까지 업로드할 파일과 같은 이름의 파일이 존재하는지 확인, 있다면 이름을 바꾸고, 없다면 그대로 진행한 다음,

파일을 업로드하고, 업로드한 파일 이름을 VO에 set까지 했다.

이제 업로드한 파일에 대한 정보를 DB에 등록하자. Service, ServiceImpl, DAO, 쿼리문까지 생성하자.

Service

package com.poby.myapp.service;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import com.poby.myapp.dao.DataDAO;
import com.poby.myapp.vo.DataVO;

@Service
public class DataServiceImpl implements DataService {
	@Inject
	DataDAO dao;

	@Override
	public int dataPostOk(DataVO vo) {
		return dao.dataPostOk(vo);
	}
}

쿼리문 생성

<insert id="dataPostOk">
	INSERT INTO data_tbl(postno, subject, content, username, filename1, filename2) 
	VALUES(board_seq.nextval, #{subject}, #{content}, #{username}, #{filename1}, #{filename2})
</insert>

컨트롤러 모델&뷰

다시 컨트롤러로 돌아와서, 업로드 실패 시 이미 업로드된 파일을 삭제시키고 뒤로가기 실행, 성공하면 목록으로 이동시킨다.

// 자료 올리기 페이지
@PostMapping("/data/dataPostOk")
public ModelAndView dataPostOk(HttpServletRequest req, DataVO vo) {
	// HttpServletRequest객체를 이용하여 form의 값과 파일객체를 서버로 가져온다
	ModelAndView mav = new ModelAndView();
	
	// 작성자
	vo.setUsername((String)req.getSession().getAttribute("logUsername"));
	
	// 파일 업로드
	String path = req.getSession().getServletContext().getRealPath("/upload");
	
	// req객체를 이용하여 MultipartHttpServletRequest객체를 생성하여 파일 업로드를 처리한다
	// MultipartHttpServletRequest객체에는 파일 수 만큼 MultipartFile객체가 존재한다
	MultipartHttpServletRequest mr = (MultipartHttpServletRequest)req;
	
	// mr에서 MultipartFile객체를 구해온다
	List<MultipartFile> fileList = mr.getFiles("filename");
	
	// 첨부한 파일이 있을 때---------------------------------------------
	// 업로드한 파일명을 보관할 변수
	List<String> filenameList = new ArrayList<String>();
	if(fileList!=null) {
		// 첨부파일의 개수만큼 반복한다************************************
		for(int i=0; i<fileList.size(); i++) {
			MultipartFile mf = fileList.get(i);	// List에서 파일얻어오기
			String file = mf.getOriginalFilename();	// 업로드 시 선택한 원래파일명 얻어오기
			
			// 서버에 filename이 없으면 그대로 업로드하고, 있으면 새로운 파일명 생성한다
			if(file!=null && !file.equals("")) {	// 원래 파일명이 존재하면
				File f = new File(path, file);
				if(f.exists()) {	// 파일이 존재하는지 알려준다 -> true:있다, false:없다
					// 같은 이름의 파일이 있음 -> 파일명 변경 : file(1).txt
					for(int renameNum=1;;renameNum++) {
						int dot = file.lastIndexOf(".");	// 파일명, 확장자 분리
						String filename = file.substring(0, dot);	// 파일명
						String extension = file.substring(dot+1);	// 확장자
						
						// 새로운 파일명
						String newFile = filename+"("+renameNum+")."+extension;
						f = new File(path, newFile);
						
						// 기존 파일 중 새로운 파일명과 같은 파일이 없으면 반복문 중단
						if(!f.exists()) {
							file = newFile;
							break;
						}
					}
					
				}
				// 업로드하기
				try {
					mf.transferTo(new File(path, file));
				}catch(IOException e) {
					System.out.println("파일 업로드 예외발생");
					e.getMessage();
				}
				// 업로드한 파일명(원래파일명 or 새로운 파일명)
				filenameList.add(file);
			}
			
		}// *********************************************************
		
		// 업로드한 파일명을 vo의 filename1, filename2에 set한다
		vo.setFilename1(filenameList.get(0));
		if(filenameList.size()==2) vo.setFilename2(filenameList.get(1));
	}//---------------------------------------------------------
	System.out.println("f1 ->" + vo.getFilename1());
	System.out.println("f2 ->" + vo.getFilename2());
	
	// DB에 파일 정보 추가
	int result = service.dataPostOk(vo);
	
	// 추가 실패하면 이미 업로드된 파일을 삭제해야한다
	if(result==0) {
		for(int i=0; i<filenameList.size(); i++) {
			File delFile = new File(path, filenameList.get(i));
			delFile.delete();
		}
		mav.setViewName("data/dataPostOk");
	}else {
		mav.setViewName("redirect:dataList");
	}
	
	return mav;
}

뷰페이지

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<script>
	alert("자료 올리기 실패");
	history.back();
</script>

이제 파일을 올려보자.

파일을 두 개 올리기

파일 하나만 올리기

※ 500에러 파일을 하나만 올렸을때 에러

파일 두 개 중 하나만 첨부해서 올렸을때 생긴 오류다. 쿼리문에서 filename2에 대한 매핑이 안 됐다는 메시지이다.

dataMapper.xml에 가서 쿼리문에 조건문을 붙여줘야한다.

<insert id="dataPostOk">
	INSERT INTO data_tbl(postno, subject, content, username, filename1<if test="filename2!=null">, filename2</if>) 
	VALUES(board_seq.nextval, #{subject}, #{content}, #{username}, #{filename1}<if test="filename2!=null">, #{filename2}</if>)
</insert>

똑같은 이름의 파일 올리기