오라클,스프링프레임워크 기준입니다.
먼저 파일추가에 필요한 테이블을 추가해준다.
CREATE TABLE MP_FILE
(
FILE_NO NUMBER,
BNO NUMBER NOT NULL,
ORG_FILE_NAME VARCHAR2(260) NOT NULL,
STORED_FILE_NAME VARCHAR2(36) NOT NULL,
FILE_SIZE NUMBER,
REGDATE DATE DEFAULT SYSDATE NOT NULL,
DEL_GB VARCHAR2(1) DEFAULT 'N' NOT NULL,
PRIMARY KEY(FILE_NO)
);
CREATE SEQUENCE SEQ_MP_FILE_NO
START WITH 1
INCREMENT BY 1
NOMAXVALUE NOCACHE;
COMMIT;
첨부파일 기능에 필요한 파라미터는 파일번호, 게시판번호, 원본파일이름, 변경된 파일이름, 파일크기, 파일등록일,삭제구분 이 되겠다. 물론 기능추가를 위해 파라미터는 변경될 수 있다.
PR은 파일번호이고
파일이 어느 게시글에서 첨부됐는지 구분하기 위한 게시글번호(게시글PR)
클라이언트에서 보낸 원본파일의 이름
DB에 저장하기 위한 변경된 파일의 이름(원본파일의 이름으로 저장하기엔 중복같은 여러 문제가 발생함)
파일크기와 등록일,
게시판에 첨부파일이 들어가있는 글을 올린후 수정페이지에서 첨부파일을 삭제했을때의 여부를 알려줌. DB엔 파일이 남아있고 게시글에만 표시되지 않게 함.
파일번호를 +1시키는 시퀸스도 만들어준다.
<!-- 첨부파일 START-->
<!-- MultipartHttpServletRequset -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<!-- 첨부파일 END -->
pom.xml에 라이브러리를 추가해준다.
클라이언트(뷰)에서 서버에 파일을 업로드하기위해 파라미터를 보내면 ( 뒤에 multipart 인코딩 타입으로 파라미터 보내는 방법을 설명) 받은 데이터를 알맞게 여러가지 처리를 해줘야한다.
multipartResolver는 multipart라는 인코딩타입을 지원하는 스프링의 기능(라이브러리) 인데, 추가적인 처리없이 multipart형식으로 전송된 파라미터와 파일정보를 쉽게 구할 수 있다.
/WEB-INF/spring에 context-common.xml 생성 후 코드추가
?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- MultipartResolver 설정 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000000" />
<property name="maxInMemorySize" value="100000000" />
</bean>
</beans>
라이브러리를 폼에 추가했으니 사용하기 위해 스프링 설정파일에 등록해주도록 하자.
스프링에서 제공하는 MultipartResolver는 CommonsMultipartResolver 라는 이름이다.
빈객체를 등록하고,
property에
maxUploadSize = 최대 업로드 가능한 바이크 크기, 기본값은 -1이고 , 제한이 없음을 의미함
maxInMeomorySize = 디스크에 임시 파일을 생성하기 전에 메모리에 보관할 수 있는 최대 바이트 크기 , 기본값은 10240바이트
-------
라이브러리를 pom.xml에 등록하고 설정파일에 빈과 설정까지 등록해줬으니 이제 이 부품을 스프링이 인식할 수 있도록 web.xml에 /WEB-INF/spring/context-common.xml 로 경로를 지정해준다 (/WEB-INF이하 설정파일 경로)
그리고
이렇게까지 하면 MultipartResolver를 사용하기 위한 설정이 끝난다.
util 패키지를 만들어주고 FileUtils.java 파일을 생성한다.
@Component
public class FileUtils {
private static final String filePath = "C:\\mp\\file\\"; // 파일이 저장될 위치
public List<Map<String, Object>> parseInsertFileInfo(BoardVO boardVO,
MultipartHttpServletRequest mpRequest) throws Exception{
Iterator<String> iterator = mpRequest.getFileNames();
MultipartFile multipartFile = null;
String originalFileName = null;
String originalFileExtension = null;
String storedFileName = null;
List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
Map<String, Object> listMap = null;
int bno = boardVO.getBno();
File file = new File(filePath);
if(file.exists() == false) {
file.mkdirs();
}
while(iterator.hasNext()) {
multipartFile = mpRequest.getFile(iterator.next());
if(multipartFile.isEmpty() == false) {
originalFileName = multipartFile.getOriginalFilename();
originalFileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
storedFileName = getRandomString() + originalFileExtension;
file = new File(filePath + storedFileName);
multipartFile.transferTo(file);
listMap = new HashMap<String, Object>();
listMap.put("BNO", bno);
listMap.put("ORG_FILE_NAME", originalFileName);
listMap.put("STORED_FILE_NAME", storedFileName);
listMap.put("FILE_SIZE", multipartFile.getSize());
list.add(listMap);
}
}
return list;
}
public static String getRandomString() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
}
첨부파일의 정보를 이용해서 서버에 저장하고 다운로드,업로드등의 기능을 구현하기 위해 여러가지 조작을 할 클래스인데,
서비스나 컨트롤러에 직접 구현하면 너무 코드가 복잡해지니 따로 파일을 만들어서 메서드를 꺼내쓰도록 하자.
위에서 아래로 내려오며 설명을 해보겠다.
먼저
@Component는 클래스를 빈으로 등록한다.
위에 multipartResolver를 빈으로 등록한 것 처럼, 해당 클래스를 스프링설정파일 파일에 빈으로 등록하고 경로를 지정해줘서 사용할 수도 있지만,
@Component 어노테이션을 붙여주면 좀 더 간단하게 사용할 수 있다. 빈으로 등록하는 부분을 생략하고, 어노테이션 위치 설정인 component-scan에 파일 경로를 올려주면 된다.
라이브러리는 어노테이션을 붙일 수 없으니 빈으로 직접 등록해서 사용하고 경로지정
( 라이브러리등록[pom.xml] - 빈등록[context-common.xml] - web.xml의 contextConfigLocation에 경로지정)
클래스는 어노테이션을 붙이고 경로지정
( @Component 어노테이션 등록 - web.xml의 contextConfigLocation에 지정된 xml 안의 component-scan으로 경로지정[ root-context.xml] )
파일업로드는 첨부파일값(multipart 인코딩타입으로 전송되는 값)을 파라미터로 처리해주는 multipartResolver(라이브러리) 라는 부품과 FileUtils(클래스) 라는 부품으로 구현하고,
이 두개 부품의 설정방법의 차이를 설명해보았다.
filePath는 서버에 파일이 저장될 위치를 지정한다.
-----
Iterator 는 아래 사이트를 참고했다.
이터레이터(Iterator) 란? (tistory.com)
public List<Map<String, Object>> parseInsertFileInfo(BoardVO boardVO,
MultipartHttpServletRequest mpRequest)
부분을 보면
List는 순차적인 데이터의 집합이고 Map은 키와 값으로 이루어진 순서가 유지되지 않은 데이터의 집합이다.
Iterator를 사용하면 동일한 방식으로 접근이 가능해서 항목에 접근할 수 있는 방법을 제공한다.
hasNext() : 읽어올 요소가 남아있는지 확인하는 메소드이다. 요소가 있으면 true, 없으면 false
next() : 다음 데이터를 반환한다.
remove() : next()로 읽어온 요소를 삭제한다.
---
Iterator<String> iterator = mpRequest.getFileNames();
***************
mpReqeust(MultipartResolver라이브러리에서 지원하는 MultipartHttpServletReqeust 인터페이스의 파일 관련 주요 메서드)
Iterator<String> getFileNames()
업로드 된 파일들의 이름 목록을 제공하는 Iterator를 구한다.
MultipartFile getfile(String name)
파라미터 이름이 name이 업로드 파일 정보를 구한다.
List<MultipartFile> getFiles(String name)
파라미터 이름이 name인 업로드 파일 정보 목록을 구한다.
Map<String, MultipartFile> getFileMap()
파라미터 이름을 키로 파라미터에 해당하는 파일 정보를 값으로 하는 Map을 구한다.
************
업로드 된 파일들의 이름 목록을 제공하는 iterator(생성자) 이라는 이름의 Iterator을 구한다
MultipartFile multipartFile = null;
String originalFileName = null;
String originalFileExtension = null;
String storedFileName = null;
파일 업로드에 필요한 파라미터들 ( 처음에 만든 파일테이블의 속성)
int bno = boardVO.getBno();
파일 업로드에 필요한 파라미터중 하나인 게시글PR
-----
File file = new File(filePath);
if(file.exists() == false) {
file.mkdirs();
}
filePath로 설정한 경로들에 폴더가 존재하지 않으면 mkdirs() 로 폴더 생성
위의 코드는 C:\\mp\\file\\ 설정해놨는데 아무 폴더도 생성하지 않고 첨부파일을 업로드해도
정상처리 후 확인해보면 c드라이브 - mp 폴더 안에 file폴더가 생성되어 파일이 저장되어 있다.
while(iterator.hasNext()) {
multipartFile = mpRequest.getFile(iterator.next());
if(multipartFile.isEmpty() == false) {
originalFileName = multipartFile.getOriginalFilename();
originalFileExtension = originalFileName.substring(originalFileName.lastIndexOf("."));
storedFileName = getRandomString() + originalFileExtension;
file = new File(filePath + storedFileName);
multipartFile.transferTo(file);
listMap = new HashMap<String, Object>();
listMap.put("BNO", bno);
listMap.put("ORG_FILE_NAME", originalFileName);
listMap.put("STORED_FILE_NAME", storedFileName);
listMap.put("FILE_SIZE", multipartFile.getSize());
list.add(listMap);
}
}
업로드된 파일의 이름목록에 읽어올 요소가 있다면 multipartFile 이라는 이름으로 다음 데이터를 반환하고,
multipartFile.isEmpty() , isEmpty()는 값이 비어있는지 확인해서 비어있으면 true이다.
그러므로 다음 읽어올 요소의 데이터가 값이 있다면
업로드할때 필요한 데이터 ( 테이블의 속성) 값들을 가져온다.
RandomStirng()은 서버에 데이터가 저장될때 중복으로 저장되어 오류가 날 경우를 피하기위해 저장될 이름을 따로 생성한다.
getOriginalFilename(); 은 업로드된 파일의 원본파일 이름을 가져오는 인터페이스고
originalFileName.lastIndexOf("."))은 가져온 원본파일이름뒤에 이름을 붙이는 함수다, 이걸 아래의
getRandomString()과 함께 사용해 storedFileName을 만든다.
저장경로 + 저장될이름의 데이터가 담긴 file을 선언하고
multipartFile.transferTo(file); 지정한 경로에 저장될 이름으로 저장한다.
-----------
이제 받아온 파일을 지정한 경로에 저장했으니 데이터베이스에 저장하는데 필요한 값들을 put으로 Map에 넣어준다.
클라이언트에서 업로드한 파일은 지정한 저장소에 저장 후 데이터베이스에 기록하는 흐름이다.
put으로 넣은 데이터들로 지정한 경로에 저장할 파일을 분류해서 업로드, 다운로드 등 조작을 할 수 있게 된다.
---
중복을 피할 수 있는 랜덤한 문자를 생성해주는 getRandomString()을 만들어준다.
---------------------
root-context.xml에 추가
<context:component-scan base-package="kr.co.util"></context:component-scan>
클래스 빈을 사용
@RequestMapping(value = "/write", method = RequestMethod.POST)
public String write(BoardVO boardVO, MultipartHttpServletRequest mpRequest) throws Exception{
logger.info("write");
service.write(boardVO, mpRequest);
return "redirect:/board/list";
}
컨트롤러 부분.
게시판 글작성 컨트롤러 부분에 첨부파일을 인코딩해서 파라미터 값을 받아올수 있게 하는 MultiparthttpServletRquest를 추가해준다.
BoardService.java
public void write(BoardVO boardVO, MultipartHttpServletRequest mpRequest) throws Exception;
------------------
BoardServiceImpl.java
@Inject(Autowired)
private FileUtils fileUtils;
@Inject
private BoardDAO dao;
@Override
public void write(BoardVO boardVO, MultipartHttpServletRequest mpRequest) throws Exception {
dao.write(boardVO);
List<Map<String,Object>> list = fileUtils.parseInsertFileInfo(boardVO, mpRequest);
int size = list.size();
for(int i=0; i<size; i++){
dao.insertFile(list.get(i));
}
}
서비스 부분이다.
아까 작성한 글작성쿼리에 mapper의 위치와 컨트롤러에서 받아온 파라미터를 넘겨주는 dao.write와
파일첨부쿼리인 dao.insertFile을 넣어준다.
다중 첨부파일을 위해
for문으로 size번만큼 반복할 수 있게 한다.
BoardDAO.java
public void insertFile(Map<String, Object> map) throws Exception;
BoardDAOImpl.java
@Override
public void insertFile(Map<String, Object> map) throws Exception {
// TODO Auto-generated method stub
sqlSession.insert("boardMapper.insertFile", map);
}
첨부파일DAO이다.
<form name="writeForm" role="form" action="/board/write" method="post" enctype="multipart/form-data">
<div class="table-responsive" style="text-align:center;">
<table id="datatable-scroller"
class="table table-bordered tbl_Form">
<caption></caption>
<colgroup>
<col width="250px" />
<col />
</colgroup>
<tbody>
<tr>
<th class="active" >작성자</th>
<td class="form-inline"><input type="text" id="writer"
name="writer" class="chk" title="작성자을 입력하세요." style="width: 200px" />
</td>
</tr>
<tr>
<th class="active">제목</th>
<td class="form-inline"><input type="text" id="title"
name="title" class="chk" title="제목을 입력하세요." style="width: 840px" />
</td>
</tr>
<tr>
<th class="active" >내용</th>
<td class="form-inline"><textarea
id="content" name="content" cols="100" rows="10"
class="chk" title="내용을 입력하세요."></textarea></td>
</tr>
<tr><td id="fileIndex">
<input type="file" name="file" />
<div>
<button id="fileAdd_btn" class="btn btn-primary" type="button">파일추가+</button>
</div>
</td></tr>
</tbody>
</table>
이제 뷰단이다.
글작성 뷰단의 일부분인데, form태그에 enctype="multpart/form-data"로 첨부파일값을 multpart인코딩 타입으로 매핑주소에 맞는(action=) 컨트롤러로 보낼 수 있다. 간단하다.
input type=file은 파일업로드 형식을 지원해준다.
첨부파일 insert기능을 구현해보았다
'SPRING > IceWater Community' 카테고리의 다른 글
[스프링]게시판 첨부파일 다운로드 (3) (1) | 2021.08.30 |
---|---|
[스프링] 게시글 첨부파일 조회 구현 설명(select) (2) (1) | 2021.08.30 |
[스프링]-댓글 수 구현 : 동기,비동기 방식을 사용할 때의 구현, 차이점과 2가지의 댓글 수 변경 로직 (0) | 2021.08.26 |
[스프링] 게시글 조회수와 트랜잭션 구현 (0) | 2021.08.25 |
스프링 페이징 구현 - 게시글 수정,삭제,내용보기 후 검색어유지 (1) | 2021.08.23 |