프로그래밍/스프링 & 스프링 부트

스프링 서블릿 api를 이용한 파일 업로드, 다운로드(2)-다운로드

밍구몬 2019. 6. 24. 17:31

이 예제는 기존 소스에 추가하여 만들었으며, 전체 소스는 깃허브에 올려 두었다.

github : https://github.com/ming9mon/spring

 

pom.xml

		<!-- jackson core -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.9</version>
		</dependency>

게시글 상세보기에서 JSON 데이터를 받아오기 때문에 jackson디펜던시를 추가해 준다.

servletContext.xml

<resources location="file:///D:/upload/" mapping="/img/**"></resources>

스프링에서 서버의 폴더에 접근할 수 있도록 매핑을 해준다.

업로드 폴더를 D:/upload/년/월/일 로 만들어 줬으니 /img/년/월/일 로 접근하면 된다.

 

BoardController

	// 글 상세 조회
	@RequestMapping("/getContent.do")
	public String getBoard(BoardVO vo, Model model, @ModelAttribute("cri") Criteria cri) throws Exception{
		model.addAttribute("board", boardService.getContent(vo)); // Model 정보 저장
		return "content"; // View 이름 리턴
	}

	// 글 수정 페이지
	@RequestMapping("/modify.do")
	public String modify(BoardVO vo, Model model, @ModelAttribute("cri") Criteria cri) throws Exception{
		System.out.println(vo.getIdx());
		model.addAttribute("board", boardService.getContent(vo)); // Model 정보 저장
		return "modify"; // View 이름 리턴
	}
    
    
	//첨부파일 리스트 JSON으로 받기
	@GetMapping(value="/getAttachList", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
	@ResponseBody
	public ResponseEntity<List<FileVO>> getAttachList(Long bno){
		return new ResponseEntity<>(boardService.getAttachList(bno),HttpStatus.OK);
	}
	
	@GetMapping(value="/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
	@ResponseBody
	public ResponseEntity<Resource> downloadFile(String fileName,String origin, @RequestHeader("User-Agent")String userAgent){
		Resource resource = new FileSystemResource("D:"+File.separator+"upload"+File.separator+fileName);
		String resourceName = resource.getFilename();
		
		if(resource.exists() == false) {
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		}
		
		
		HttpHeaders headers = new HttpHeaders();
		try {
			
			String downloadName = null;
			
			//user-Agent의 정보를 파라미터로 받아 브라우저별 처리
			if(userAgent.contains("Trident")) {	//IE 브라우저 엔진 이름
				System.out.println("IE");
				downloadName = URLEncoder.encode(origin, "UTF-8").replaceAll("\\+"," ");
			}else if(userAgent.contains("Edge")) {
				System.out.println("Edge");
				downloadName = URLEncoder.encode(origin, "UTF-8");
			}else{	//크롬
				System.out.println("chrom");
				downloadName = new String(origin.getBytes("UTF-8"), "ISO-8859-1");
			}
			
			headers.add("Content-Disposition", "attachment; filename="+downloadName);
			
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		
		return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
	}

기존에는 게시글 상세보기랑 수정하기를 한페이지로 만들었었는데 다운로드 기능을 추가하기 위하여 상세보기와 수정하기 페이지를 나누었다.

 

getAttachList는 jsp에서 ajax로 첨부된 파일 리스트를 보여주기 위하여 만들어 주었으며, JSON 형태로 값을 리턴한다.

 

downloadFile에서 MIME 타입은 다운로드를 할 수 있는 "application/octet-stream"으로 지정하고, 다운로드 시 저장되는 이름은 "Content-Disposition"을 이용해 지정하였다.

파일 이름이 한글일 경우 저장할 대 깨지는 문제를 해결하기 위해 파일 이름에 대한 문자열 처리를 해준다.

브라우저마다 인코딩 방식이 다르기 때문에 브라우저별로 처리를 해주었다.

 

downloadFile에서는 파라미터를 fileName과 origin 두 개와 user-agent의 정보를 받는다.

fileName은 서버에 저장되어있는 파일명(UUID+확장자)이고, origin은 원본 파일명이다.

user-agent의 정보로 접속 브라우저를 확인하여 원본 파일명을 알맞게 인코딩해 리턴해준다.

content.jsp

			<tr>
				<td colspan="2" align="center">
				<h3><b>첨부파일</b></h3>
					<div id="preview"></div>
				</td>
			</tr>

<script type="text/javascript">
	$(document).ready(function (e){
		
		//첨부파일 다운로드
		(function(){
			var bno='<c:out value="${board.idx}"/>';
			$.getJSON("/getAttachList", {bno: bno}, function(arr){

				var arrLength = arr.length;
				//첨부파일이 없을 경우
				if(arrLength == 0){
					console.log(arr.length);
					var str = "<span>첨부파일 없음</span>";
					$(str).appendTo('#preview');
				}
				$(arr).each(function(i, attach){
					
					var imgPath = '/img/'+attach.path+'/'+attach.uuid;
					//파일명이 길면 파일명...으로 처리
					var fileName = attach.fileName;
					if(fileName.length > 10){
						fileName = fileName.substring(0,7)+"...";
					}
					
					//div에 첨부파일 이미지 추가
					var fileCallpath = encodeURIComponent(attach.path+"/"+attach.uuid);
					var origin = encodeURIComponent(attach.fileName);
					console.log(fileCallpath);
					var str = '<div style="display: inline-flex; padding: 10px;"><li>';
					str += '<a href="/download?fileName='+fileCallpath+'&origin='+origin+'">'+fileName+'</a><br>';
					str += '<img src="'+imgPath+'" title="'+attach.fileName+'" title="'+attach.fileName+'" onerror="this.src=\'/resources/img/fileImg.png\'" width=100 height=100 />';
					str += '</li></div>';
					$(str).appendTo('#preview');
					
				});//arr each
			});	//end getJSON
		})(); //end function
		
	});
	
</script>
</html>

content.jsp 에서 첨부파일을 표시할 행을 추가시켜준뒤, jQuery로 작업을 한다.

익명 함수를 사용하여 페이지가 로딩되었을 때, $.getJSON으로 게시글 번호를 보내 첨부파일 리스트를 받아온다.

 

첨부 파일이 없을 때는 첨부파일 없음이라고 추가시켜 주고, 첨부 파일이 있는경우 첨부 파일의 이미지를 보여준다. 

첨부 파일의 이미지도 이전과 같이 이미지 파일이면 해당 이미지를 보여주고, 일반 파일이면 파일 모양의 이미지를 보여주는데 이번에는 img태그의 onerror를 이용하여 파일 이미지를 보여주었다.

 

Content.jsp 전체 소스

...더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>글 상세</title>
	<script src="resources/jQuery/jquery-3.4.1.min.js"></script>
</head>
<body>
	<h1>글 상세</h1>
	<hr>
	<form id="frm" action="modify.do" method="get">
		<input name="idx" type="hidden" value="${board.idx}" />
		<table border="1">
			<tr>
				<td bgcolor="orange" width="70">제목</td>
				<td align="left">${board.title }</td>
			</tr>
			<tr>
				<td bgcolor="orange">작성자</td>
				<td align="left">${board.writer }</td>
			</tr>
			<tr>
				<td bgcolor="orange">내용</td>
				<td align="left"><textarea id="content" name="content" cols="40" rows="10" readonly="readonly">${board.content }</textarea></td>
			</tr>
			<tr>
				<td bgcolor="orange">등록일</td>
				<td align="left"><fmt:formatDate value="${board.regDate }" pattern="yyyy-MM-dd"/></td>
			</tr>
			<tr>
				<td colspan="2" align="center">
				<h3><b>첨부파일</b></h3>
					<div id="preview"></div>
				</td>
			</tr>
			<tr>
				<td bgcolor="orange">조회수</td>
				<td align="left">${board.cnt }</td>
			</tr>
			<tr>
				<td colspan="2" align="center"><input type="submit" value="글 수정" /></td>
			</tr>
		</table>
	</form>
	<hr>
	<a href="writeBoard.do">글 쓰기</a>&nbsp;&nbsp;&nbsp; 
	<a href="deleteBoard.do?idx=${board.idx }">글 삭제</a>&nbsp;&nbsp;&nbsp;
	<a id="list" href="getBoardList.do">글 목록</a>
</body>
<script type="text/javascript">
	$(document).ready(function (e){
		
		//글 목록
		$('#list').click(function(e){
			e.preventDefault();
			var $form = $('<form></form>');
			$form.attr('action','getBoardList.do');
			$form.attr('method','get');
			$form.appendTo('body');
			
			$form.append("<input type='hidden' name='pageNum' value='<c:out value='${cri.pageNum}'/>'>");
			$form.append("<input type='hidden' name='amount' value='<c:out value='${cri.amount}'/>'>");
			$form.submit();
		});
		
		//첨부파일 다운로드
		(function(){
			var bno='<c:out value="${board.idx}"/>';
			$.getJSON("/getAttachList", {bno: bno}, function(arr){

				var arrLength = arr.length;
				//첨부파일이 없을 경우
				if(arrLength == 0){
					console.log(arr.length);
					var str = "<span>첨부파일 없음</span>";
					$(str).appendTo('#preview');
				}
				$(arr).each(function(i, attach){
					
					var imgPath = '/img/'+attach.path+'/'+attach.uuid;
					//파일명이 길면 파일명...으로 처리
					var fileName = attach.fileName;
					if(fileName.length > 10){
						fileName = fileName.substring(0,7)+"...";
					}
					
					//div에 첨부파일 이미지 추가
					var fileCallpath = encodeURIComponent(attach.path+"/"+attach.uuid);
					var origin = encodeURIComponent(attach.fileName);
					console.log(fileCallpath);
					var str = '<div style="display: inline-flex; padding: 10px;"><li>';
					str += '<a href="/download?fileName='+fileCallpath+'&origin='+origin+'">'+fileName+'</a><br>';
					str += '<img src="'+imgPath+'" title="'+attach.fileName+'" title="'+attach.fileName+'" onerror="this.src=\'/resources/img/fileImg.png\'" width=100 height=100 />';
					str += '</li></div>';
					$(str).appendTo('#preview');
					
				});//arr each
			});	//end getJSON
		})(); //end function
		
	});
	
</script>
</html>