실습
- [Web_Spring] 21 이어서
1. src/test/java/com.example.board/mapper/AttachMapperTests.java
package com.example.board.mapper;
import com.example.board.domain.vo.AttachFileVO;
import com.example.board.domain.vo.BoardVO;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@Slf4j
public class AttachMapperTests {
@Autowired
private AttachMapper attachMapper;
@Autowired
private BoardMapper boardMapper;
@Test
public void insertTest(){
BoardVO boardVO = boardMapper.select(5221L);
AttachFileVO attachFileVO = new AttachFileVO();
attachFileVO.setFileName("uuid_테스트.txt");
attachFileVO.setOriginalFileName("테스트.txt");
attachFileVO.setUploadDirectory("2022/06/30");
attachFileVO.setImage(false);
attachFileVO.setBoardVO(boardVO);
attachMapper.insert(attachFileVO);
}
}
2. src/main/java/com.example.board/service/BoardServiceImpl.java
package com.example.board.service;
import com.example.board.domain.dao.AttachDAO;
import com.example.board.domain.dao.BoardDAO;
import com.example.board.domain.vo.AttachFileVO;
import com.example.board.domain.vo.BoardVO;
import com.example.board.domain.vo.Criteria;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
// Service
// 여러 개의 DML을 하나의 서비스로 묶어준다.
@Service
@RequiredArgsConstructor
@Qualifier("board") @Primary
public class BoardServiceImpl implements BoardService{
private final BoardDAO boardDAO;
private final AttachDAO attachDAO;
@Override
public List<BoardVO> getList(Criteria criteria) {return boardDAO.getList(criteria);}
@Override
// 하나의 트랜잭션에 여러 개의 DML이 있을 경우 한 개라도 오류 시 전체 ROLLBACK
@Transactional(rollbackFor = Exception.class)
public void register(BoardVO boardVO) {
boardDAO.register(boardVO);
if(boardVO.getFileList() != null){
boardVO.getFileList().forEach(fileVO -> {
fileVO.setBoardVO(boardVO);
attachDAO.save(fileVO);
});
}
}
@Override
public BoardVO get(Long boardNumber) {
return boardDAO.findByBoardNumber(boardNumber);
}
@Override
public boolean modify(BoardVO boardVO) {
return boardDAO.modify(boardVO);
}
@Override
public boolean remove(Long boardNumber) {
return boardDAO.remove(boardNumber);
}
@Override
public int getTotal(Criteria criteria) {
return boardDAO.getTotal(criteria);
}
}
3. src/main/java/com.example.board/controller/UploadController.java
package com.example.board.controller;
import com.example.board.domain.vo.AttachFileVO;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnailator;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
// 1. 동일한 이름으로 파일이 업로드 되면, 기존 파일 삭제
// 2. 이미지 파일의 경우 원본 파일의 용량이 클 수 있기 때문에 썸네일 이미지가 필요하다.
// 3. 첨부파일 공격에 대비하기 위한 업로드 파일의 확장자 제한
@Controller
@Slf4j
@RequestMapping("/upload/*")
public class UploadController {
// ajax를 사용한 파일 업로드
@GetMapping("/uploadAjax")
public void uploadAjax(){
log.info("upload ajax");
}
@ResponseBody //REST
@PostMapping("/uploadAjax")
public List<AttachFileVO> uploadAjax(MultipartFile[] files) throws IOException{
List<AttachFileVO> fileList = new ArrayList<>();
String rootDirectory = "C:/upload";
File uploadDirectory = new File(rootDirectory, getDateDirectory());
if(!uploadDirectory.exists()) {uploadDirectory.mkdirs();}
for (MultipartFile file : files){
log.info("------------------------------------");
log.info("upload file name : " + file.getOriginalFilename());
log.info("upload file size : " + file.getSize());
AttachFileVO attachFileVO = new AttachFileVO();
UUID uuid = UUID.randomUUID();
String fileName = uuid.toString() + "_" + file.getOriginalFilename();
attachFileVO.setOriginalFileName(file.getOriginalFilename());
attachFileVO.setFileName(fileName);
attachFileVO.setUploadDirectory(getDateDirectory());
File saveFile = new File(uploadDirectory, fileName);
file.transferTo(saveFile);
if(checkImageType(saveFile)){
FileOutputStream thumbnail = new FileOutputStream(new File(uploadDirectory, "t_" + fileName));
// MultipartFile객체를 통해 바로 파일을 가져올 경우,
// 임시로 저장될 영역을 임계 영역이라 한다.
// apllication.properties에서 임계 영역에 대한 용량을 설정해 주어야
// 그 영역에 먼저 업로드 후 inputStream()을 가져올 수 있다.
Thumbnailator.createThumbnail(file.getInputStream(), thumbnail, 100, 100);
thumbnail.close();
attachFileVO.setImage(true);
}
fileList.add(attachFileVO);
}
return fileList;
}
@GetMapping("display")
@ResponseBody
public byte[] getFile(String path) throws IOException {
return FileCopyUtils.copyToByteArray(new File("C:/upload/" + path));
}
@GetMapping("/download")
@ResponseBody
public ResponseEntity<Resource> download(String path) throws UnsupportedEncodingException {
Resource resource = new FileSystemResource("C:/upload/" + path);
String name = resource.getFilename();
name = name.substring(name.indexOf("_") + 1);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=" + new String(name.getBytes("UTF-8"), "ISO-8859-1"));
return new ResponseEntity<Resource>(resource, headers, HttpStatus.OK);
}
@DeleteMapping("/delete")
@ResponseBody
public void deleteFile(String path){
// 썸네일 삭제
File file = new File("C:/upload", path);
if(file.exists()) {file.delete();}
// 원본파일 삭제
file = new File(file.getPath().replace("t_", ""));
if(file.exists()) {file.delete();}
}
private String getDateDirectory(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date = new Date();
String directory = sdf.format(date);
return directory;
}
private boolean checkImageType(File file) throws IOException{
return Files.probeContentType(file.toPath()).startsWith("image");
}
}
'웹 개발 > Spring' 카테고리의 다른 글
[Web_Spring] 21 (0) | 2022.07.03 |
---|---|
[Web_Spring] 20 (0) | 2022.07.02 |
[Web_Spring] 23 (0) | 2022.07.01 |
[Web_Spring] 19 (0) | 2022.07.01 |
[Web_Spring] 18 (0) | 2022.06.30 |