1. fileUpload 라이브러리 -> pom.xml에 추가
- 필요한 라이브러리
- commens-fileUpload 1.4
- commins-io 2.11.0
- thumbnailator 0.4.14
- tika-core 1.28 ( tika : 확장자 확인 용도 )
- tika-parsers 1.28
<!-- 파일 입출력 -->
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-core -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>1.28</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-parsers -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>1.28</version>
</dependency>
▲ tica-core, tica-parsers의 1.28 버전
▼ tica-core, tica-parsers의 2.4.1 버전
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-core -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tika/tika-parsers -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers</artifactId>
<version>2.4.1</version>
<type>pom</type>
</dependency>
추가하고, 다른 설정에 영향을 미치는 값이 있는지 생각해보고 넘어가기~!
2. file 경로 설정 xml 추가
1 ) 파일을 업로드 할 경로를 외부에 설정 ( 폴더를 따로 생성하기 )
경로 : D:\_myweb\_java\_fileUpload
2 ) web.xml에 파일 경로 및 설정 설정
- 위치 : appServlet의 <servlet>태그 안, <load-on-startup>태그 아래
web.xml 파일 설정시 추가 설정
1. 파일의 최대 크기 설정 : 1M - 2M
2. 요청에 따른 응답(request) 크기 설정 : 4M - 5M -10M
3. file크기 여분 메모리 size 설정 : 1M - 2M
( M = mega , 1M = 1*1024*1024 , 2M = 2*1024*1024 )
( 20971520 = 20M )
- 추가 부분
<multipart-config>
<location>D:\_myweb\_java\_fileUpload</location>
<max-file-size>20971520</max-file-size>
<max-request-size>41943040</max-request-size>
<file-size-threshold>20971520</file-size-threshold>
</multipart-config>
- 추가 후 전체
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/appServlet/servlet-context.xml
/WEB-INF/spring/appServlet/security-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<location>D:\_myweb\_java\_fileUpload</location>
<max-file-size>20971520</max-file-size>
<max-request-size>41943040</max-request-size>
<file-size-threshold>20971520</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encoding</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<filter-mappring>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mappring>
</filter>
</web-app>
3 ) servlet-context.xml에 파일 경로 맵핑, multipartResolver bean 설정
- 추가 부분
- 파일 경로 맵핑
<resources location="file:///D:\_myweb\_java\_fileUpload\" mapping="/upload/**" />
- multipartResolver bean 설정
<beans:bean id="mutipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</beans:bean>
- 추가 후 전체
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 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 location="file:///D:\_myweb\_java\_fileUpload\" mapping="/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" /><!-- 경로의 뒤에 붙여서 jsp파일이 되도록 -->
</beans:bean>
<context:component-scan base-package="com.myweb.www" />
<beans:bean id="mutipartResolver"
class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</beans:bean>
</beans:beans>
3. DB 생성 ) file을 별도로 나타낼 수 있는 table 생성
uuid(범용 고유 식별자) : random한 고유 번호를 설정
create table file(
uuid varchar(256) not null,
save_dir varchar(256) not null,
file_name varchar(256) not null,
file_type tinyint(1) default 0,
bno int,
file_size int,
reg_date datetime default now(),
primary key(uuid)
);
webapp - resource - sql에 구문 추가
4. domain패기지에 FileVO ( class 파일 ) 생성
package com.myweb.www.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Setter
@Getter
public class FileVO {
/*
* create table file(
uuid varchar(256) not null,
save_dir varchar(256) not null,
file_name varchar(256) not null,
file_type tinyint(1) default 0,
bno int,
file_size int,
reg_date datetime default now(),
primary key(uuid)
);
* */
private String uuid;
private String save_dir;
private String file_name;
private int file_type;
private int bno;
private int file_size;
private String reg_date;
}
5. board폴더의 register.jsp에 파일 부분 추가
- 추가 부분
<form action="/board/register" method="post" enctype="mutipart/form-data">
...
file : <input type="file" name="file" multiple>
<!-- multiple : 다중파일 업로드 가능 (파일을 배열처리해서 여러개를 가져감) -->
<button type="button">fileUpload</button>
<!-- 파일에 대한 업로드가 가능하도록 설정하는 버튼 -->
<div id="filezone">
<!-- 파일 목록이 리스트로 올라가는 부분 -->
</div>
- 추가 후 전체 부분
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Register Page</title>
</head>
<body align="center">
<h1>Register Page</h1>
<form action="/board/register" method="post" enctype="multipart/form-data">
title : <input type="text" name="title" placeholder="제목"> <br>
writer : <input type="text" name="writer" value="${ses.id }"> <br>
content : <br>
<textarea rows="10" cols="50" name="content"></textarea><br>
file : <input type="file" name="file" id="file" multiple>
<!-- multiple : 다중파일 업로드 가능 (파일을 배열처리해서 여러개를 가져감) -->
<button type="button" id="trigger">fileUpload</button> <br>
<!-- 파일에 대한 업로드가 가능하도록 설정하는 버튼 -->
<div id="filezone">
<!-- 파일 목록이 리스트로 올라가는 부분 -->
</div>
<button type="submit">등록</button> <br>
</form>
<a href="/"><button type="button">home</button></a>
</body>
6. webapp - resourse - js폴더에 boardRegister.js 생성
- fileUpload버튼을 클릭하면, file이 클릭되는 효과 설정하기
- 왜 따로 버튼을 만들고 연결해? css꾸미기를 위해서!
document.getElementById("trigger").addEventListener('click',()=>{
document.getElementById('file').click();
})
7. board폴더의 register.jsp에 js연결코드 작성
- 추가
<script type="text/javascript" src="/resource/js/boardRegister.js"></script>
- 추가 후 전체
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Register Page</title>
</head>
<body align="center">
<h1>Register Page</h1>
<form action="/board/register" method="post" enctype="multipart/form-data">
title : <input type="text" name="title" placeholder="제목"> <br>
writer : <input type="text" name="writer" value="${ses.id }"> <br>
content : <br>
<textarea rows="10" cols="50" name="content"></textarea><br>
file : <input type="file" name="file" id="file" multiple>
<!-- multiple : 다중파일 업로드 가능 (파일을 배열처리해서 여러개를 가져감) -->
<button type="button" id="trigger">fileUpload</button> <br>
<!-- 파일에 대한 업로드가 가능하도록 설정하는 버튼 -->
<div id="filezone">
<!-- 파일 목록이 리스트로 올라가는 부분 -->
</div>
<button type="submit">등록</button> <br>
</form>
<a href="/"><button type="button">home</button></a>
<script type="text/javascript" src="/resources/js/boardRegister.js">
</script>
</body>
8. boardRegister.js 에 이미지파일 유무 설정하는 코드 작성
- 상단 click연결 부분 제외 모두 작성 부분임
document.getElementById("trigger").addEventListener('click',()=>{
document.getElementById('file').click();
})
// 정규표현식을 사용하여 생성자 함수를 만들기
// 실행파일을 막고, 이미지파일 유무 체크
//생성자함수의 시작은 \.
const regExp = new RegExp("\.(exe|sh|bat|msi|dll|js)$"); //들어오면 안되는 형식
const regExpImg = new RegExp("\.(jpg|jpeg|png|gif|bmp)$"); //이미지 파일 형식
const maxSize = 1024*1024*20; //20M
function fileSizeValidation(fileName, fileSize){
if(regExp.test(fileName)){ //test : 맞는지 아닌지 확인! 맞으면 true (들어오면 안되는 형식의 경우)
return 0;
}else if(fileSize > maxSize){ //파일의 사이즈가 너무 큰 경우
return 0;
}else if(!regExpImg.test(fileName)){//이미지 파일이 아니면 첨부 X
return 0;
}else{
return 1;
}
}
//image file에 따라서 체크 (등록 가능 여부)
document.addEventListener('change',(e)=>{
console.log("e.target : " + e.target);
if(e.target.id == 'file'){
document.getElementById('regBtn').disabled = false;//등록버튼 열기
// 첨부되지 말하야하는 파일이 들어왔을 경우 전송되는 것을 방지 (등록버튼 사용 X)
const fileObject = document.getElementById('file').file;
console.log("fileObject : " + fileObject);
//filezone
let div = document.getElementById('filezone');
div.innerHTML='';
let ul = `<ul>`;
let isOk = 1; //파일의 통과여부(fileSizeValidation) - 통과면 1
for(let file of fileObject){
let vaildResult = fileSizeValidation(file.name, file.size); //여러파일이 들어간 배열
isOk *= vaildResult; //곱하기? 하나라도 아니면 0을 곱하면 0이니까(모든 파일이 1이여 통과)
ul += `<li>`; //li태그 열고
//업로드 가능 표시 작성 ( 1 = true )
ul += `<div> ${vaildResult ? '업로드 가능' : '업로드 불가능'} </div>`;
ul += `${file.name}`;
ul += `<span>${file.size}Byte</span>`;
ul += `</li>`;
}
ul += `</ul>`;
div.innerHTML = ul;
if(isOk == 0){ //첨부 불가파일이 있는 경우
document.getElementById('regBtn').disabled = true; //등록막기
}
}
})
<ul>
//첨부파일이 2개 이상인 경우, 이 위치에 for문을 돌려 li를 여러개 생성할 예정
<li> //li하나당 하나의 파일
<div>업로드 가능</div>
<div>업로드 불가능</div>
파일의 이름
<span>파일 사이즈</span>
</li>
</ul>
9. board-register.jsp의 등록 버튼에 id 생성
- 파일이 맞다면 regBtn이 disabled=false하고 (등록버튼 클릭 가능)
- 파일이 아니라면 regBtn이 disabled=true설정 (등록버튼 클릭 불가능)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Register Page</title>
</head>
<body align="center">
<h1>Register Page</h1>
<form action="/board/register" method="post" enctype="multipart/form-data">
title : <input type="text" name="title" placeholder="제목"> <br>
writer : <input type="text" name="writer" value="${ses.id }"> <br>
content : <br>
<textarea rows="10" cols="50" name="content"></textarea><br>
file : <input type="file" name="file" id="file" multiple>
<!-- multiple : 다중파일 업로드 가능 (파일을 배열처리해서 여러개를 가져감) -->
<button type="button" id="trigger">fileUpload</button> <br>
<!-- 파일에 대한 업로드가 가능하도록 설정하는 버튼 -->
<div id="filezone">
<!-- 파일 목록이 리스트로 올라가는 부분 -->
</div>
<button type="submit" id="regBtn">등록</button> <br>
</form>
<a href="/"><button type="button">home</button></a>
<script type="text/javascript" src="/resources/js/boardRegister.js">
</script>
</body>
== 여기까지 출력화면 ==
10. board-register.jsp의 파일 선택 버튼을 안보이게 변경
+ name을 files로 변경
file : <input type="file" name="files" id="file" multiple style="display:none">
11. BoardController의 registerPost메서드 파일 저장 구문 추가
@PostMapping("/register")
public String registerPost(BoardVO bvo, Model m, RedirectAttributes rAttr,
@RequestParam(name="files", required = false)MultipartFile[] files) {
//RedirectAttributes : 컨트롤 안에서 이동이 가능하도록 하는 역할 + 휘발성 사용을 위해서 이용 (데이터의 새로고침)
//required : 필수여부 = 해당 param이 필수로 가져와야하는가 확인 ( false = 해당 파라미터가 없더라도 예외가 발생하지 않음 )
log.info(">>> bvo "+bvo.toString());
log.info("files : " + files.toString());
List<FileVO> fList = null;
//file 처리 -> handler에서 만들어서 호출!
int isOk = bsv.register(bvo);
log.info(">>> board register >> "+( isOk > 0 ? "OK" : "Fail"));
rAttr.addFlashAttribute("isOk", isOk); //일회성으로 사용!
// return "redirect:/board/list";
// return "redirect:/member/login"; //이런 방법으로 같은 패키지의 타 controller로 이동도 가능
return "redirect:/";
// /는 홈으로 이동
// redirect사용 : insert, update, delete
}
12. 파일처리를 할 FileHandler ( class파일 ) Handler패키지에 생성
package com.myweb.www.Handler;
import java.io.Console;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.tika.Tika;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.myweb.www.domain.FileVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnailator;
import net.coobird.thumbnailator.Thumbnails;
@Slf4j
@AllArgsConstructor
@Component
public class FileHandler {
private static final Logger log = LoggerFactory.getLogger(FileHandler.class);
private final String UP_DIR = "D:\\_myweb\\_java\\_fileUpload";
public List<FileVO> uploadFiles(MultipartFile[] files){
//---------------- 1. 가져온 파일들을 저장할 폴더를 생성 ----------------------
//일자별로 파일을 정리할 예정 ( date )
LocalDate date = LocalDate.now();
log.info(">>> date : " + date);
String today = date.toString(); //date객체를 string으로 변환 ( 2023-06-14 형태 )
// 년도안의 월안의 일 폴더로 계층적 폴더를 만들예정 2023\06\14(window) , 2023/06/14(ios/linux)
today = today.replace("-", File.separator);//today를 '-'기준으로 자르기
//today로 폴더 구성 (경로 구성)
File folders = new File(UP_DIR, today);
//폴더가 기존에 있다면 생성x, 없다면 생성
if(!folders.exists()) {//폴더가 없는 경우
folders.mkdirs(); //폴더 생성 명령어 (실제 폴더 생성)
}
//경로 설정
List<FileVO> fList = new ArrayList<FileVO>();
for(MultipartFile file : files) {
// --------- fvo에 저장할 정보 생성 (set) ----------
FileVO fvo = new FileVO();
fvo.setSave_dir(today); //파일 경로 넣기
fvo.setFile_size(file.getSize()); //파일 사이즈 설정
//경로가 포함되어 있을 수도 있는 파일 명
String originalFileName = file.getOriginalFilename();
log.info("originalFileName :" + originalFileName);
//실제 파일 명
String onlyFileName = originalFileName.substring(
originalFileName.lastIndexOf(File.separator)+1);
log.info("onlyFileName : " + onlyFileName);
//파일의 이름 설정
fvo.setFile_name(onlyFileName);
//UUID(java에서 기본 제공) 생성 + 설정
UUID uuid = UUID.randomUUID();
fvo.setUuid(uuid.toString());
// --------- 디스크에 저장할 객체 생성 (저장) ----------
//실제 저장할 full 파일이름
String fullfileName = uuid.toString()+"_"+onlyFileName;
//폴더와 이름을 담아 생성
File storeFile = new File(folders, fullfileName);
try {
//원본 객체를 저장할 위한 형태의 객체로 복사
file.transferTo(storeFile);
//파일의 타입 결정
if(isImgFile(storeFile)) { //이미지인 경우
//파일 타입을 1로 변경 (default값으로 0설정되어있음)
fvo.setFile_type(1);
//썸네일 설정
//썸네일 파일의 경로
File thumbNail = new File(folders, uuid.toString()+"_"+onlyFileName);
//썸네일 생성
Thumbnails.of(storeFile).size(75, 75).toFile(thumbNail);
}
} catch (Exception e) {
log.info(">>> 파일 생성 오류 발생");
e.printStackTrace();
}
//fList에 생성한 fvo넣고
fList.add(fvo);
}
//fList리턴
return fList;
}
//tika를 이용한 파일 형식 체크 메서드 (여기서만 사용할 예정이라 private)
//이미지파일이 맞으면 true아니면 false 리턴
private boolean isImgFile(File storeFile) throws IOException {
//해당하는 이미지의 타입을 빼기 (image/jpg, image/png 모양의 string형태)
String mimeType = new Tika().detect(storeFile);
log.info(mimeType);
//startWith : 앞에 원하는 제시어 있는지 확인 (image가 앞에 있으면 tru리턴)
return mimeType.startsWith("image")? true : false;
}
}
13. BoardController에서 파일 처리구문 작성
- controller 상단에 @Inject private FileHandler fhd; 로 FileHandler import!
- registerPost메서드에 파일 처리 구문 작성
@Inject
private FileHandler fhd;
@PostMapping("/register")
public String registerPost(BoardVO bvo, Model m, RedirectAttributes rAttr,
@RequestParam(name="files", required = false)MultipartFile[] files) {
//RedirectAttributes : 컨트롤 안에서 이동이 가능하도록 하는 역할 + 휘발성 사용을 위해서 이용 (데이터의 새로고침)
//required : 필수여부 = 해당 param이 필수로 가져와야하는가 확인 ( false = 해당 파라미터가 없더라도 예외가 발생하지 않음 )
log.info(">>> bvo "+ bvo.toString());
log.info("files : " + files.toString());
List<FileVO> fList = null;
//file 처리 -> handler에서 만들어서 호출!
if(files[0].getSize()>0) {//가져온 파일이 있는 경우
//fhd.uploadFiles(files) : 파일 배열을 경로설정, fvo set해서 리스트로 리턴
fList = fhd.uploadFiles(files);
}else {
log.info("file null 파일 없음");
}
//파일과 보드 처리를 같이할건지 별도로 할것인지 선택!
// int isOk = bsv.register(bvo);
log.info(">>> board register >> "+( isOk > 0 ? "OK" : "Fail"));
rAttr.addFlashAttribute("isOk", isOk);
return "redirect:/board/list";
}
14. file과 board bvo처리를 같이 할 BoardDTD생성
'Spring > Spring-eclipse' 카테고리의 다른 글
[spring] security 1.com.myweb.www.config설정 (0) | 2023.06.22 |
---|---|
[spring] security 0. DB생성, pom.xml작성 (0) | 2023.06.22 |
[spring] 댓글 2. 댓글 출력 (0) | 2023.06.12 |
[spring] 댓글 1. 댓글 작성 (0) | 2023.06.09 |
[spring] 댓글 0. 테이블 생성, STS 기본 생성 (0) | 2023.06.09 |