본문 바로가기
👨‍💻 2. 웹개발_Back end/2-6 Spring

[Spring] 9장 스프링 게시판 만들기 - 첨부 파일 업로드, 수정, 삭제, 다운로드

by 달님🌙 2021. 11. 4.
반응형

 

실습 코드 참조

 

moonhy7/SpringFramework: Spring Framework 실습 코드 정리 (github.com)

 

GitHub - moonhy7/SpringFramework: Spring Framework 실습 코드 정리

Spring Framework 실습 코드 정리. Contribute to moonhy7/SpringFramework development by creating an account on GitHub.

github.com

 

 

1. 파일 업로드 ( _82_BoardWeb_Spring_MVC_FileUpload )

 

1. 테이블 생성

CREATE TABLE BOARD_FILE (
	SEQ INTEGER,
    F_SEQ INTEGER,
    ORIGINAL_FILE_NAME VARCHAR(255),
    FILE_PATH VARCHAR(500),
    FILE_SIZE INTEGER,
    CONSTRAINT BOARD_FILE_PK PRIMARY KEY(SEQ, F_SEQ)
);

 

2. sql-map-config

- settings 태그 추가

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
</configuration>

 

3. BoardFileVO 클래스 생성

package com.springbook.biz.board;

public class BoardFileVO {
	private int seq;
	private int fseq;
	private String originalFileName;
	private String filePath;
	private long fileSize;
	public int getSeq() {
		return seq;
	}
	public void setSeq(int seq) {
		this.seq = seq;
	}
	public int getFseq() {
		return fseq;
	}
	public void setFseq(int fseq) {
		this.fseq = fseq;
	}
	public String getOriginalFileName() {
		return originalFileName;
	}
	public void setOriginalFileName(String originalFileName) {
		this.originalFileName = originalFileName;
	}
	public String getFilePath() {
		return filePath;
	}
	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}
	public long getFileSize() {
		return fileSize;
	}
	public void setFileSize(long fileSize) {
		this.fileSize = fileSize;
	}
	
	@Override
	public String toString() {
		return "BoardFileVO [seq=" + seq + ", fseq=" + fseq + ", originalFileName=" + originalFileName + ", filePath="
				+ filePath + ", fileSize=" + fileSize + "]";
	}
}

 

4. presentation-layer

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<!-- 업로드 파일 최대 크기 설정, 기본 값: -1 -> 크기 제한없음 -->
	<property name="maxUploadSize" value="-1"></property>
</bean>

 

5. getBoard.jsp

- multiple 속성 추가, uploadFile -> uploadFiles로 변경 

<tr>
  <td bgcolor="orange" width="70">업로드</td>
  <td align="left">
  	<input type="file" name="uploadFile" multiple="multiple">
  </td>
</tr>

 

6. FileUtils 생성

- 먼저 common 패키지 JDBCUtil 빼고 다 삭제

 

- 그 다음 common 패키지에 FileUtils 클래스 생성 (여러개의 파일을 담아서 리스트로 던져줄 객체 생성)

package com.springbook.biz.common;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.springbook.biz.board.BoardFileVO;

public class FileUtils {
	public List<BoardFileVO> parseFileInfo(int seq, HttpServletRequest request, 
			MultipartHttpServletRequest mhsr) throws IOException {
		if(ObjectUtils.isEmpty(mhsr)) {
			return null;
		}
		
		List<BoardFileVO> fileList = new ArrayList<BoardFileVO>();
		
		//서버의 절대 경로 얻기
		String root_path = request.getSession().getServletContext().getRealPath("/");
		String attach_path = "/upload/";
		
		//위 경로의 폴더가 없으면 폴더 생성
		File file = new File(root_path + attach_path);
		if(file.exists() == false) {
			file.mkdir();
		}
		
		//파일 이름들을 iterator로 담음
		Iterator<String> iterator = mhsr.getFileNames();
		
		while(iterator.hasNext()) {
			//파일명으로 파일 리스트 꺼내오기
			List<MultipartFile> list = mhsr.getFiles(iterator.next());
			
			//파일 리스트 개수 만큼 리턴할 파일 리스트에 담아주고 생성
			for(MultipartFile mf : list) {
				BoardFileVO boardFile = new BoardFileVO();
				boardFile.setSeq(seq);
				boardFile.setFileSize(mf.getSize());
				boardFile.setOriginalFileName(mf.getOriginalFilename());
				boardFile.setFilePath(root_path + attach_path);
				fileList.add(boardFile);
				
				file = new File(root_path + attach_path + mf.getOriginalFilename());
				mf.transferTo(file);
			}
		}
		return fileList;
	}
}

 

7. BoardController 수정

@RequestMapping("/insertBoard.do")
	//Command 객체 : 사용자가 전송한 데이터를 매핑한 VO를 바로 생성
	//				사용자 입력 값이 많아지면 코드가 길어지기 때문에 간략화 가능
	//              사용자 입력 input의 name 속성과 VO 멤버변수의 이름을 매핑해주는 것이 중요
	public String insertBoard(BoardVO vo, HttpServletRequest request,
			MultipartHttpServletRequest mhsr) throws IOException {
		System.out.println("글 등록 처리");
		
		//파일 업로드 처리
		MultipartFile uploadFile = vo.getUploadFile();
		if(!uploadFile.isEmpty()) {
			String fileName = uploadFile.getOriginalFilename();
			uploadFile.transferTo(new File("C:/Dev211/" + fileName));
		}
		
		int seq = BoardService.getBoardSeq();
		
		FileUtils fileUtils = new FileUtils();
		List<BoardFileVO> fileList = fileUtils.parseFileInfo(seq, request, mhsr);
		
		boardService.insertBoard(vo);
		
		//화면 네비게이션(게시글 등록 완료 후 게시글 목록으로 이동)
		return "redirect:getBoardList.do";
	}

 

8. BoardService 수정

//글 목록 조회
List<BoardVO> getBoardList(BoardVO vo, Criteria cri);

int selectBoardCount(BoardVO vo);

//글 등록 전 등록 될 일련번호 획득
int getBoardSeq();

//디비에 파일 리스트 등록
void insertBoardFileList(List<BoardFileVO> fileList);

 

9. 실행 결과

- 새글 등록

- 파일 선택

- 글 목록에 추가

- 첨부파일 목록에 추가

 

 

2. 파일 수정 및 삭제

 

1. BoardController

@RequestMapping(value="/getBoard.do")
public String getBoard(BoardVO vo, Model model, Criteria cri) {
  System.out.println("글 상세 조회 처리");

  System.out.println(cri.getPageNum());
  System.out.println(cri.getAmount());
  //Model 객체는 RequestServlet 데이터 보관소에 저장
  //RequestServlet 데이터 보관소에 저장하는 것과 동일하게 동작
  //request.setAttribute("board", boardDAO.getBoard(vo)) == model.addAttribute("board", boardDAO.getBoard(vo))
  model.addAttribute("board", boardService.getBoard(vo));
  model.addAttribute("criteria", cri);
  model.addAttribute("fileList", boardService.getBoardFileList(vo.getSeq()));
  return "getBoard.jsp";
}

 

2. BoardService

//디비에 파일 리스트 등록
void insertBoardFileList(List<BoardFileVO> fileList);

//파일목록 리턴
List<BoardFileVO> getBoardFileList(int seq);

 

3. BoardServiceImpl

public List<BoardFileVO> getBoardFileList(int seq) {
	return boardDAO.getBoardFileList(seq);
}

 

4. board-mapping

<select id="getBoardFileList" parameterType="int" resultType="boardFile">
  SELECT * FROM BOARD_FILE
  WHERE SEQ = #{seq}
</select>

 

5. BoardDAOMybatis

	public void insertBoardFileList(List<BoardFileVO> fileList) {
		for(BoardFileVO boardFile : fileList) {
			mybatis.insert("BoardDAO.insertBoardFileList", boardFile);
		}
	}
	
	public List<BoardFileVO> getBoardFileList(int seq) {
		return mybatis.selectList("BoardDAO.getBoardFileList", seq);
	}
	
	public void deleteFile(BoardFileVO vo) {
		mybatis.delete("BoardDAO.deleteFile", vo);
	}
	
	public void updateFile(BoardFileVO vo) {
		mybatis.update("BoardDAO.updateFile", vo);
	}
	
	public void deleteFileList(int seq) {
		mybatis.delete("BoardDAO.deleteFileList", seq);
	}

 

6. 실행 화면

 

 

3. 파일 미업로드 시 발생하는 에러 해결법

 

1. FileUtils 코드 수정

while(iterator.hasNext()) {
  //파일명으로 파일 리스트 꺼내오기
  List<MultipartFile> list = mhsr.getFiles(iterator.next());

  //파일 리스트 개수 만큼 리턴할 파일 리스트에 담아주고 생성
  for(MultipartFile mf : list) {
    if(mf.getSize() > 0) {
      BoardFileVO boardFile = new BoardFileVO();
      boardFile.setSeq(seq);
      boardFile.setFileSize(mf.getSize());
      boardFile.setOriginalFileName(mf.getOriginalFilename());
      boardFile.setFilePath(root_path + attach_path);
      fileList.add(boardFile);

      file = new File(root_path + attach_path + mf.getOriginalFilename());
      mf.transferTo(file);
      } else {
      fileList = null;
    }
  }
}

 

 

4. 파일 다운로드

 

1. getBoardList.jsp 

<tr>
  <td bgcolor="orange">첨부파일 목록</td>
  <td>
    <c:forEach var="file" items="${fileList }">
      <a class="downlink" id="${file.fSeq }" href="${file.originalFileName }">${file.originalFileName }</a>
      <button type="button" onclick="deleteFile('${file.fSeq }');">삭제</button>
      <br>
    </c:forEach>
  </td>
</tr>

 

2. getBoardList.jsp 스크립트에 function 추가

<script>
$(document).ready(function)() {
  $(".downlink").click(function(e){
    e.preventDefault();
    var fileName = $(this).attr("href");
    window.location = "fileDown.do?fileName=" + fileName; 
  });
});
</script>

 

3. BoardController

@RequestMapping(value="fileDown.do")
	@ResponseBody
	public ResponseEntity<Resource> fileDown(@RequestParam("fileName") String fileName,
						HttpServletRequest request) throws Exception {
		//업로드 파일 경로
		String path = request.getSession().getServletContext().getRealPath("/") + "/upload/";
		
		//파일경로, 파일명으로 리소스 객체 생성
		Resource resource = new FileSystemResource(path + fileName);
		
		//파일 명
		String resourceName = resource.getFilename();
		
		//Http헤더에 옵션을 추가하기 위해서 헤더 변수 선언
		HttpHeaders headers = new HttpHeaders();
		
		try {
			//헤더에 파일명으로 첨부파일 추가
			headers.add("Content-Disposition", "attachment; filename=" + new String(resourceName.getBytes("UTF-8"),
						"ISO-8859-1"));
		} catch(UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
	}

 

4. 실행 화면

 

5. 다운로드된 파일 경로

 

 

5. 게시글 삭제시 해당 게시글 첨부파일 남아있는 현상 해결

 

1. boardService

//게시글 삭제시 해당 게시글의 첨부파일 삭제
	void deleteFileList(int seq);

 

2. boardServiceImpl

public void deleteFileList(int seq) {
	boardDAO.deleteFileList(seq);
}

 

3. BoardDAOMybatis

public void deleteFileList(int seq) {
	mybatis.delete("BoardDAO.deleteFileList", seq);
}

 

4. board-mapping

<delete id="deleteFileList" parameterType="int">
  DELETE FROM BOARD_FILE
  WHERE SEQ = #{seq}
</delete>

 

5. boardController

@RequestMapping(value="/deleteBoard.do")
	public String deleteBoard(BoardVO vo) {
		System.out.println("글 삭제 처리");
		
		boardService.deleteFileList(vo.getSeq());
		boardService.deleteBoard(vo);
		return "redirect:getBoardList.do";
	}

 

6. 실행 결과

- 첨부파일 목록에 파일들이 있는 상태로 글 삭제를 누른다.

- 그 후 다시 글 등록을 하면 해당 seq로 글 등록이 되므로 파일 목록이 그대로 남아있게 된다. 

- 위 코드를 추가하면 이제 글을 삭제했다가 다시 등록을 해도 첨부파일이 남아있지 않게 된다.

 

 

반응형

댓글