ㅁ main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
form > label {
font-size: 12px;
color: gray;
}
</style>
</head>
<body>
<script>
$(function(){
$("input[type=file]").on('change', function(evt){
const files = evt.target.files; // FileList {0:File, 1:File, ...}
console.log(files);
let totalSize = 0;
for(let i=0; i<files.length; i++){
if(files[i].size > 10 * 1024 * 1024) {
alert("첨부파일의 최대 크기는 10MB입니다.");
evt.target.value = ""; // 선택한 파일 초기화
return; // 10MB 초과라면 여기서 function 빠져나가기
}
}
totalSize += files[i].size; // 선택한 전체 파일 용량 알아내기
if(totalSize > 100 * 1024 * 1024) {
alert("전체 첨부파일 최대 크기는 100MB입니다.");
evt.target.value = ""; // 선택한 파일 초기화
return; // function 빠져나가기
}
})
})
</script>
<h2>1. 한 개의 첨부파일 업로드 테스트</h2>
<form action="${ contextPath }/board/insert1.do" method="post" enctype="multipart/form-data">
게시글 제목 : <input type="text" name="boardTitle"> <br>
게시글 내용 : <textarea name="boardContent"></textarea> <br>
첨부파일 : <input type="file" name="uploadFile"> <br>
<label>첨부파일 사이즈는 10MB 이하여야 됩니다.</label> <br><br>
<button type="submit">등록</button>
</form>
<h2>2. 다중 첨부파일 업로드 테스트</h2>
<form action="${ contextPath }/board/insert2.do" method="post" enctype="multipart/form-data">
게시글 제목 : <input type="text" name="boardTitle"> <br>
게시글 내용 : <textarea name="boardContent"></textarea> <br>
첨부파일 : <input type="file" name="uploadFile" multiple> <br>
<label>각 첨부파일 사이즈는 10MB 이하, 총 100MB 이하여야 됩니다.</label> <br><br>
<button type="submit">등록</button>
</form>
<h2>3. 비동기식으로 첨부파일 업로드 테스트</h2>
<div id="async_text">
게시글 제목 : <input type="text" id="title"> <br>
게시글 내용 : <textarea id="content"></textarea> <br>
첨부파일 : <input type="file" id="file"> <br><br>
<button id="ajax_btn">등록</button>
</div>
<script>
$(function(){
$("#ajax_btn").on("click", function(){
// ajax 게시글 등록 (요청시 전달값 : 제목, 내용, 첨부파일)
// 첨부파일을 전달해야할 경우 자바스크립트의 FormData 객체(가상의 form요소다.)에 담아서 전달
let formData = new FormData();
formData.append("boardTitle", document.getElementById("title").value);
formData.append("boardContent", document.getElementById("content").value);
formData.append("uploadFile", document.getElementById("file").files[0]); // File객체 담기
$.ajax({
url: '${contextPath}/board/ajaxInsert.do',
type: 'post', // 파일 넘어갈땐 항상 post 방식
data: formData,
processData: false, // processData : false 선언시 formData를 string으로 변환하지 않음.
contentType: false, // contentType : false 선언시 multipart/form-data로 전송되게 하는 옵션.
success: function(result){
if(result == "SUCCESS"){
alert("성공적으로 등록!");
}else{
alert("등록 실패!");
}
},
error: function(){
}
})
})
})
</script>
<h2>4. 첨부파일 목록 조회</h2>
<button onclick="fn_selectAttachList();">첨부파일 조회</button>
<div id="result"></div>
<script>
function fn_selectAttachList(){
$.ajax({
url: '${contextPath}/board/atlist.do',
success: function(resData){
console.log(resData); // [{}, {}]
let a = '';
for(let i=0; i<resData.length; i++){
a += '<a>' + resData[i].originalName + '</a><br>'
}
$('#result').html(a);
}
})
}
</script>
</body>
</html>
- "4. 첨부파일 목록 조회" 부분 추가.
ㅁ BoardController
package com.br.file.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import com.br.file.service.BoardService;
import com.br.file.util.FileUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/board")
@RequiredArgsConstructor
@Controller
public class BoardController {
private final BoardService boardService;
private final FileUtil fileUtil;
@PostMapping("/insert1.do")
public String insertOneFileBoard(BoardDto board, MultipartFile uploadFile) {
log.debug("board: {}", board); // 잘 담겼는지 확인용 로그 출력
log.debug("attach: {}", uploadFile); // 잘 담겼는지 확인용 로그 출력
AttachDto attach = null;
if(uploadFile != null && !uploadFile.isEmpty()) { // 첨부파일이 존재할 경우 => 업로드
// 전달된 파일 업로드 처리
// FileUitl 클래스의 fileupload 메소드를 호출 (uploadFile을 넘기면서)
Map<String, String> map = fileUtil.fileupload(uploadFile);
// (4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
attach = AttachDto.builder()
.filePath(map.get("filePath"))
.originalName(map.get("originalFilename"))
.filesystemName(map.get("filesystemName"))
.build();
}
int result = boardService.insertOneFileBoard(board, attach);
if(result > 0) {
log.debug("게시글 등록 성공");
}else {
log.debug("게시글 등록 실패");
}
return "redirect:/"; // 성공이든 실패든 메인페이지 요청
}
@PostMapping("/insert2.do")
public String insertManyFileBoard(BoardDto board, List<MultipartFile> uploadFile) {
List<AttachDto> list = new ArrayList<>();
for(MultipartFile file : uploadFile) {
if(file != null && !file.isEmpty()) { // 파일이 존재할 경우
Map<String, String> map = fileUtil.fileupload(file);
list.add(AttachDto.builder()
.filePath(map.get("filePath"))
.originalName(map.get("originalFilename"))
.filesystemName(map.get("filesystemName"))
.build());
}
}
// 넘어온 첨부파일이 없었다면 list는 비어있는 상태다.
int result = boardService.insertManyFileBoard(board, list);
// 첨부파일이 없었다면 result는 board에만 insert되고 result는 1이다.
// 첨부파일이 있었다면 첨부파일의 개수만큼
if(list.isEmpty() && result == 1 || !list.isEmpty() && result == list.size()) {
log.debug("다중 첨부파일 게시글 등록 성공");
}else {
log.debug("다중 첨부파일 게시글 등록 실패");
}
return "redirect:/"; // 성공이든 실패든 메인페이지 요청
}
@ResponseBody
@PostMapping("/ajaxInsert.do")
public String insertAjaxFileBoard(BoardDto board, MultipartFile uploadFile) {
AttachDto attach = null;
if(uploadFile != null && !uploadFile.isEmpty()) {
Map<String, String> map = fileUtil.fileupload(uploadFile);
attach = AttachDto.builder()
.filePath(map.get("filePath"))
.originalName(map.get("originalFilename"))
.filesystemName(map.get("filesystemName"))
.build();
}
int result = boardService.insertOneFileBoard(board, attach);
if(result > 0) {
log.debug("ajax 첨부파일 게시글 등록 성공");
return "SUCCESS";
}else {
log.debug("ajax 첨부파일 게시글 등록 실패");
return "FAIL";
}
}
@ResponseBody
@GetMapping(value="/atlist.do", produces="application/json")
public List<AttachDto> selectAttachList() {
return boardService.selectAttachList(); // success 함수의 매개변수로 리턴된다.
}
}
ㅁ BoardServiceImpl
package com.br.file.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.br.file.dao.BoardDao;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService {
private final BoardDao boardDao;
@Override
public int insertOneFileBoard(BoardDto board, AttachDto attach) {
int result = boardDao.insertBoard(board);
if(result > 0 && attach != null) {
result = boardDao.insertAttach(attach);
}
return result;
}
@Override
public int insertManyFileBoard(BoardDto board, List<AttachDto> list) {
int result = boardDao.insertBoard(board);
if(result > 0 && !list.isEmpty()) {
result = 0;
for(AttachDto attach : list) {
result += boardDao.insertAttach(attach);
}
}
return result;
}
@Override
public List<AttachDto> selectAttachList() {
return boardDao.selectAttachList();
}
}
ㅁ BoardDao
package com.br.file.dao;
import java.util.List;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Repository
public class BoardDao {
private final SqlSessionTemplate sqlSession;
public int insertBoard(BoardDto board) {
return sqlSession.insert("boardMapper.insertBoard", board);
}
public int insertAttach(AttachDto attach) {
return sqlSession.insert("boardMapper.insertAttach", attach);
}
public List<AttachDto> selectAttachList() {
return sqlSession.selectList("boardMapper.selectAttachList");
}
}
- sql문으로 따로 전달할 건 없다. 조회된 결과를 바로 반환한다.
ㅁ board-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="boardMapper">
<resultMap id="attachResultMap" type="AttachDto">
<result column="file_no" property="fileNo" />
<result column="file_path" property="filePath" />
<result column="original_name" property="originalName" />
<result column="filesystem_name" property="filesystemName" />
<result column="ref_board_no" property="refBoardNo" />
</resultMap>
<insert id="insertBoard">
insert
into board
(
board_no
, board_title
, board_content
)
values
(
seq_bno.nextval
, #{boardTitle}
, #{boardContent}
)
</insert>
<insert id="insertAttach">
insert
into attachment
(
file_no
, file_path
, original_name
, filesystem_name
, ref_board_no
)
values
(
seq_fno.nextval
, #{filePath}
, #{originalName}
, #{filesystemName}
, seq_bno.currval
)
</insert>
<select id="selectAttachList" resultMap="attachResultMap">
select
file_no
, file_path
, original_name
, filesystem_name
, ref_board_no
from attachment
</select>
</mapper>
- resultMap은 최상단에 몰아서 작성한다.
- type에는 풀클래스명이나 별칭을 써야 한다.
- 조회된게 있으면 뽑혀서 담긴다.
column 속성값은 오타 있어도 오류 안난다.
property 속성값(담는 필드명)에 오타 있으면 오류 난다.
ㅁ
- 첨부파일 조회 버튼 클릭하면 현재 db의 attachment 테이블의 데이터들의 원본파일명이 조회된다.
- 객체들이 담겨있는 배열의 형태다.
- 맨 윗줄이 Array로 뜨기도 함.
========================================================================================
ㅁ main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
form > label {
font-size: 12px;
color: gray;
}
</style>
</head>
<body>
<script>
$(function(){
$("input[type=file]").on('change', function(evt){
const files = evt.target.files; // FileList {0:File, 1:File, ...}
console.log(files);
let totalSize = 0;
for(let i=0; i<files.length; i++){
if(files[i].size > 10 * 1024 * 1024) {
alert("첨부파일의 최대 크기는 10MB입니다.");
evt.target.value = ""; // 선택한 파일 초기화
return; // 10MB 초과라면 여기서 function 빠져나가기
}
}
totalSize += files[i].size; // 선택한 전체 파일 용량 알아내기
if(totalSize > 100 * 1024 * 1024) {
alert("전체 첨부파일 최대 크기는 100MB입니다.");
evt.target.value = ""; // 선택한 파일 초기화
return; // function 빠져나가기
}
})
})
</script>
<h2>1. 한 개의 첨부파일 업로드 테스트</h2>
<form action="${ contextPath }/board/insert1.do" method="post" enctype="multipart/form-data">
게시글 제목 : <input type="text" name="boardTitle"> <br>
게시글 내용 : <textarea name="boardContent"></textarea> <br>
첨부파일 : <input type="file" name="uploadFile"> <br>
<label>첨부파일 사이즈는 10MB 이하여야 됩니다.</label> <br><br>
<button type="submit">등록</button>
</form>
<h2>2. 다중 첨부파일 업로드 테스트</h2>
<form action="${ contextPath }/board/insert2.do" method="post" enctype="multipart/form-data">
게시글 제목 : <input type="text" name="boardTitle"> <br>
게시글 내용 : <textarea name="boardContent"></textarea> <br>
첨부파일 : <input type="file" name="uploadFile" multiple> <br>
<label>각 첨부파일 사이즈는 10MB 이하, 총 100MB 이하여야 됩니다.</label> <br><br>
<button type="submit">등록</button>
</form>
<h2>3. 비동기식으로 첨부파일 업로드 테스트</h2>
<div id="async_text">
게시글 제목 : <input type="text" id="title"> <br>
게시글 내용 : <textarea id="content"></textarea> <br>
첨부파일 : <input type="file" id="file"> <br><br>
<button id="ajax_btn">등록</button>
</div>
<script>
$(function(){
$("#ajax_btn").on("click", function(){
// ajax 게시글 등록 (요청시 전달값 : 제목, 내용, 첨부파일)
// 첨부파일을 전달해야할 경우 자바스크립트의 FormData 객체(가상의 form요소다.)에 담아서 전달
let formData = new FormData();
formData.append("boardTitle", document.getElementById("title").value);
formData.append("boardContent", document.getElementById("content").value);
formData.append("uploadFile", document.getElementById("file").files[0]); // File객체 담기
$.ajax({
url: '${contextPath}/board/ajaxInsert.do',
type: 'post', // 파일 넘어갈땐 항상 post 방식
data: formData,
processData: false, // processData : false 선언시 formData를 string으로 변환하지 않음.
contentType: false, // contentType : false 선언시 multipart/form-data로 전송되게 하는 옵션.
success: function(result){
if(result == "SUCCESS"){
alert("성공적으로 등록!");
}else{
alert("등록 실패!");
}
},
error: function(){
}
})
})
})
</script>
<h2>4. 첨부파일 목록 조회</h2>
<button onclick="fn_selectAttachList();">첨부파일 조회</button>
<div id="result"></div>
<script>
function fn_selectAttachList(){
$.ajax({
url: '${contextPath}/board/atlist.do',
success: function(resData){
console.log(resData); // [{}, {}]
let a = '';
for(let i=0; i<resData.length; i++){
a += '<a href="${contextPath}' + resData[i].filePath + '/' + resData[i].filesystemName + '">' + resData[i].originalName + '</a><br>'
}
$('#result').html(a);
}
})
}
</script>
</body>
</html>
- 이게 저 파일을 찾기위한 주소라고 보면 된다.
실제 c드라이브의 저 폴더에 저런 이름으로 있다.
- a 링크를 클릭해본다. 404가 뜬다.
파일에 문제가 없으면 이미지 파일은 브라우저에서 보여야 하는데 보이지 않는다.
- context path는 webapp 폴더를 뜻한다.
webapp에는 upload 폴더가 없다.
ㅁ servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<resources mapping="/upload/**" location="file:///upload/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.br.file" />
</beans:beans>
- 기본적으로 a 태그 클릭하면 브라우저에서 이미지, pdf가 열리게 되어 있다.
다운로드 시키려면 다른 설정을 해야 한다.
- 요청하는 url 주소가 달라지는 건 아니다.
저 요청이 들어오면 c드라이브에 있는 그 폴더 안의 거를 제공하겠다고 등록해놨기 때문에 파일을 찾을 수 있는 것이다.
============================================================================================
ㅁ 다운로드 시키기
ㅁ main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
form > label {
font-size: 12px;
color: gray;
}
</style>
</head>
<body>
<script>
$(function(){
$("input[type=file]").on('change', function(evt){
const files = evt.target.files; // FileList {0:File, 1:File, ...}
console.log(files);
let totalSize = 0;
for(let i=0; i<files.length; i++){
if(files[i].size > 10 * 1024 * 1024) {
alert("첨부파일의 최대 크기는 10MB입니다.");
evt.target.value = ""; // 선택한 파일 초기화
return; // 10MB 초과라면 여기서 function 빠져나가기
}
}
totalSize += files[i].size; // 선택한 전체 파일 용량 알아내기
if(totalSize > 100 * 1024 * 1024) {
alert("전체 첨부파일 최대 크기는 100MB입니다.");
evt.target.value = ""; // 선택한 파일 초기화
return; // function 빠져나가기
}
})
})
</script>
<h2>1. 한 개의 첨부파일 업로드 테스트</h2>
<form action="${ contextPath }/board/insert1.do" method="post" enctype="multipart/form-data">
게시글 제목 : <input type="text" name="boardTitle"> <br>
게시글 내용 : <textarea name="boardContent"></textarea> <br>
첨부파일 : <input type="file" name="uploadFile"> <br>
<label>첨부파일 사이즈는 10MB 이하여야 됩니다.</label> <br><br>
<button type="submit">등록</button>
</form>
<h2>2. 다중 첨부파일 업로드 테스트</h2>
<form action="${ contextPath }/board/insert2.do" method="post" enctype="multipart/form-data">
게시글 제목 : <input type="text" name="boardTitle"> <br>
게시글 내용 : <textarea name="boardContent"></textarea> <br>
첨부파일 : <input type="file" name="uploadFile" multiple> <br>
<label>각 첨부파일 사이즈는 10MB 이하, 총 100MB 이하여야 됩니다.</label> <br><br>
<button type="submit">등록</button>
</form>
<h2>3. 비동기식으로 첨부파일 업로드 테스트</h2>
<div id="async_text">
게시글 제목 : <input type="text" id="title"> <br>
게시글 내용 : <textarea id="content"></textarea> <br>
첨부파일 : <input type="file" id="file"> <br><br>
<button id="ajax_btn">등록</button>
</div>
<script>
$(function(){
$("#ajax_btn").on("click", function(){
// ajax 게시글 등록 (요청시 전달값 : 제목, 내용, 첨부파일)
// 첨부파일을 전달해야할 경우 자바스크립트의 FormData 객체(가상의 form요소다.)에 담아서 전달
let formData = new FormData();
formData.append("boardTitle", document.getElementById("title").value);
formData.append("boardContent", document.getElementById("content").value);
formData.append("uploadFile", document.getElementById("file").files[0]); // File객체 담기
$.ajax({
url: '${contextPath}/board/ajaxInsert.do',
type: 'post', // 파일 넘어갈땐 항상 post 방식
data: formData,
processData: false, // processData : false 선언시 formData를 string으로 변환하지 않음.
contentType: false, // contentType : false 선언시 multipart/form-data로 전송되게 하는 옵션.
success: function(result){
if(result == "SUCCESS"){
alert("성공적으로 등록!");
}else{
alert("등록 실패!");
}
},
error: function(){
}
})
})
})
</script>
<h2>4. 첨부파일 목록 조회</h2>
<button onclick="fn_selectAttachList();">첨부파일 조회</button>
<div id="result"></div>
<script>
function fn_selectAttachList(){
$.ajax({
url: '${contextPath}/board/atlist.do',
success: function(resData){
console.log(resData); // [{}, {}]
let a = '';
for(let i=0; i<resData.length; i++){
a += '<a href="${contextPath}' + resData[i].filePath + '/' + resData[i].filesystemName + '" download="' + resData[i].originalName + '">' + resData[i].originalName + '</a><br>'
}
$('#result').html(a);
}
})
}
</script>
</body>
</html>
- download 속성을 주는것 만으로 다운 자체는 잘 된다. 그런데 파일명이 UUID로 다운된다.
- download 속성의 값으로 원본파일명을 주면 작성하면 a 태그 클릭시 원본파일명으로 다운로드 된다.