File Upload 를 위해서는 commons-fileupload 와 commons-io 라는 라이브러리가 필요 하다.
하지만 Boot 에서는 기본으로 위 라이브러리를 가지고 있기 때문에 Application.properties 에서 아래 설정만 추가 해 주면 된다.
→ 설정을 안해줄 경우 에러가 발생한다.
1개 파일의 용량 제한 (보통 5MB 정도로 잡는다.)
spring.servlet.multipart.max-file-size=50MB여러 개 파일을 올릴 경우 총 용량 제한
spring.servlet.multipart.max-request-size=500MB저장될 파일 위치
spring.servlet.multipart.location=C:/uploadspring.servlet.multipart.max-file-size=50MB
spring.servlet.multipart.max-request-size=500MB
spring.servlet.multipart.location=C:/upload
위 설정을 완료했다면 일단 file을 올려주기 위해 html 문서에 file 업로드를 위한 input 태그를 작성해보자
<form action="" method="POST" enctype="multipart/form-data">
<table>
<tr>
<th>이미지</th>
<td><input type="file" name="files" multiple="multiple"/></td>
</tr>
</table>
</form>
→ multiple : 파일을 복수개 올릴 때 사용한다.
→ enctype="multipart/form": 파일을 전송시 필수로 설정해야한다.
@RequestMapping(value="/write")
public String write(MultipartFile[] files, Model model , @RequestParam Map<String,String> param) {
logger.info("file coutn : " + files.length);
boardService.write(param, files);
return "redirect:/list";
}
→ input type="file" 에 multiple 이 설정되어 있다면 배열 형태로 받아줘야 한다.
→ MultipartFile 을 선언해주고 파라미터의 이름은 input 태그 속 name 과 동일하게 맞춰줘야 한다.
public void write(Map<String, String> param, MultipartFile[] files) {
// 3) inset와 동시에 방금 생성한 key값 가져오기
BoardDTO dto = new BoardDTO();
dto.setUser_name(param.get("user_name"));
dto.setSubject(param.get("subject"));
dto.setContent(param.get("content"));
// 3)
if(boardDAO.write(dto)>0) {
int idx = dto.getIdx();
// 1)
for (MultipartFile file : files) {
try {
// 2) 파일 이름 지정
String fileName = file.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf("."));
String newFileName = UUID.randomUUID().toString() + ext;
// 1) 배열로 담아온 값을 추출해 입력한다.
byte[] arr = file.getBytes();
Path path = Paths.get("C:/upload/"+newFileName);
Files.write(path, arr);
boardDAO.fileWrite(idx,fileName, newFileName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1) 배열로 담아온 값을 추출해 입력한다.
.getBytes()Path, Paths.get()Files.write() 2) 파일 이름 지정
getOriginalFilename().substring(fileName.lastIndexOf("."))substirng() 을 활용해 특정 인덱스 부터 문자열을 출력한다..lastIndexOf(".") : .이 있는 곳의 맨 뒤에 있는 인덱스를 가져온다.UUID.randemUUID().toString() + ext(확장자명을 담은 변수).toString() : UUID 는 UUID 객체를 반환하기 때문에 문자로 변환해야 한다.ext : UUID 는 확장자 명까지 바꿔버리기 때문에 뒤에 파일의 기존 확장자를 붙여준다.❌ 파일을 저장할 때 만약 동일한 파일명이 저장될 경우 덮어쓰기가 되므로, 유일한 이름을 부여해줘야 한다. (확장자는 기존 파일명에서 떼와야 한다)
3) inset와 동시에 방금 생성한 key값 가져오기
dto.setUser_name(param.get("user_name"));if 문으로 걸러주고, DTO에 저장된 값을 get으로 가져온다.❗ 주의사항
@Mapper
public interface BoardDAO {
List<BoardDTO> list();
}
→ Mapper 과 연결시켜 줄 추상메서드 생성
<mapper namespace="kr.co.gudi.dao.BoardDAO">
<insert id="write"
useGeneratedKeys="true"
keyColumn="idx"
keyProperty="idx"
parameterType="kr.co.gudi.dto.BoardDTO">
INSERT INTO bbs(user_name,subject,content)
VALUES(#{user_name},#{subject},#{content})
</insert>
<insert id="filewrite" parameterType="map">
INSERT INTO files(idx, ori_filename, new_filename)
VALUES(#{param1}, #{param2}, #{param3})
</insert>
</mapper>
useGeneratedKeys="true" : key generate 설정을사용할건지keyColumn="idx" : 가져올 PK 컬럼 이름keyProperty="idx" : 저장할 DTO 변수 명📌 generate key
너무 많이 사용하기 때문에 무조건 제공해주는 기능
여기까지 설정을 해주면 이제 파일을 등록하고 그 파일을 DB에 넣어 저장할 수 있다.
<table>
<c:if test="${files.size() > 0}">
<tr>
<th>이미지</th>
<td>
<c:forEach items="${files}" var="file">
<img width="500" alt="${file.ori_filename}" src="/photo/${file.new_filename}"><br/>
</c:forEach>
</tr>
</c:if>
</table>
→ file이 있을 경우에만 보여주기 위해 size 체크
→ 복수개의 파일을 업로드할 수 있기 때문에 c:forEach 사용
→ 파일이 있으면 photo에서 new_filename 경로로 파일을 가져오도록 지정
❌ 현재 서버에는 photo라는 경로가 없기 때문에 server.xml을 추가 설정해줘야 한다.
<Context docBase="C:/upload" path="/photo"/>
@RequestMapping(value="/detail")
public String detail(String idx ,Model model) {
boardService.detail(idx, model);
return "detail";
}
→ 보내줘야 하는 값이 글과 이미지 2개 인데 return은 2개를 보낼 수 없기 때문에 service 에게 Model 을 넘겨줘서 service에서 값을 전달할 수 있게 한다.
@Transactional
public void detail(String idx, Model model) {
boardDAO.uphit(idx);
BoardDTO bbs = boardDAO.detail(idx);
List<FileDTO> files = boardDAO.files(idx);
model.addAttribute("info", bbs);
model.addAttribute("files", files);
}
@Transactional 선언.uphit() 메서드 실행.detail() 메서드 실행 → PK 값을 전달해 특정한 글만 볼 수 있도록 진행.files() 메서드 실행 → 특정 글에 해당하는 파일을 가져오기 위해 PK값 전달@Mapper
public interface BoardDAO {
BoardDTO detail(String idx);
List<FileDTO> files(String idx);
}
<select id="detail" resultType="kr.co.gudi.dto.BoardDTO">
SELECT * FROM bbs WHERE idx = #{param1}
</select>
<select id="files" resultType="kr.co.gudi.dto.FileDTO">
SELECT * FROM files WHERE idx = #{param1}
</select>
<update id="uphit">
UPDATE bbs set bHit = bHit + 1 where idx=#{param1}
</update>