코딩항해기

[Spring] 파일 입출력 MultipartFile 본문

Spring

[Spring] 파일 입출력 MultipartFile

miniBcake 2024. 10. 21. 16:49

 

 

MultipartFile을 사용해 이미지 파일을 업로드할 수 있다.

먼저 MultipartFile로 이미지 파일을 업로드 받기 위해서는 몇 가지 환경을 조성해야한다.

 

DTO에 MultipartFile을 타입으로 갖는 필드 추가

해당 필드를 통해 view로부터 전송되는 MultipartFile을 자동 주입 받을 수 있게 된다.

@Getter @Setter @ToString
public class ImageDTO {
    private int imageId;
    private String path;
    private int bid;
    private MultipartFile image;
}

 

이때 MultipartFile를 자동으로 import하지 못하는 경우 의존성을 추가한다.

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.5</version>
</dependency>

 

ImageDAO, ImageServiceImpl

더보기
@Repository
public class ImageDAO {
    private final String SELECTONE = "SELECT IMAGEID, PATH, BID FROM IMAGE WHERE BID=? ORDER BY IMAGEID DESC LIMIT 1";
    private final String INSERT = "INSERT INTO IMAGE (PATH,BID) VALUES(?,?)";

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private List<ImageDTO> selectAll(ImageDTO imageDTO){
        List<ImageDTO> datas = new ArrayList<ImageDTO>();
        return datas;
    }
    public ImageDTO selectOne(ImageDTO imageDTO) {
        Object[] args = {imageDTO.getBid()};
        return jdbcTemplate.queryForObject(SELECTONE, args, new ImageMapper());
    }
    public boolean insert(ImageDTO imageDTO) {
        return jdbcTemplate.update(INSERT, imageDTO.getPath(), imageDTO.getBid())>0;
    }
    private boolean update(ImageDTO imageDTO){
        return false;
    }
    private boolean delete(ImageDTO imageDTO){
        return false;
    }
}

class ImageMapper implements RowMapper<ImageDTO>{
    @Override
    public ImageDTO mapRow(ResultSet rs, int i) throws SQLException {
        ImageDTO data = new ImageDTO();
        data.setImageId(rs.getInt("IMAGEID"));
        data.setPath(rs.getString("PATH"));
        data.setBid(rs.getInt("BID"));
        return data;
    }
}

 

 

@Service
public class ImageServiceImpl implements ImageService {
    @Autowired
    private ImageDAO imageDAO;

    @Override
    public List<ImageDTO> selectAll(ImageDTO imageDTO) {
        return List.of();
    }

    @Override
    public ImageDTO selectOne(ImageDTO imageDTO) {
        return imageDAO.selectOne(imageDTO);
    }

    @Override
    public boolean insert(ImageDTO imageDTO) {
        return imageDAO.insert(imageDTO);
    }

    @Override
    public boolean update(ImageDTO imageDTO) {
        return false;
    }

    @Override
    public boolean delete(ImageDTO imageDTO) {
        return false;
    }
}

 

View에 이미지 파일을 받을 form태그 추가

    <%--MultipartFile을 다룰 것임을 알려주는 enctype 속성--%>
    <form action="updateBoard.do" method="POST" enctype="multipart/form-data">
        <%--현재 게시글 번호--%>
        <input type="hidden" name="bid" value="${data.bid}">
        <%--입력받을 이미지--%>    
        이미지 <input type="file" name="image" onchange="preview(event)"> <br>
        <%--입력받은 이미지를 미리 보여줄 이미지 태그--%>    
        <img id="previewImage" style="display:none;margin:5px;" alt="미리보기 이미지"><br>
        <%--전송--%>    
        <input type="submit" value="이미지 변경">
    </form>

 

 

이제 이미지를 입력받는 부분과 DB를 처리하는 부분이 완성됐다.

받은 파일을 서버에 저장하고, DB에게 파일명을 전달하는 역할을 수행하는 Controller 파트를 작업하면된다.

 

Controller

	@RequestMapping("/updateBoard.do")
	public String updateBoard(BoardDTO boardDTO, ImageDTO imageDTO) throws IOException {
		//boardService.update(boardDTO); //이미지 수정만 진행할 예정
		log.info("log: /updateBoard.do updateBoard start");
		MultipartFile file = imageDTO.getImage(); //MultipartFile타입을 가진 필드에 자동 주입된 데이터를 저장
		String fileName = file.getOriginalFilename(); //MultipartFile의 이름 추출
		log.info("path : [{}]", PATH+fileName);
		
		file.transferTo(new File(PATH+fileName)); //지정된 경로에 추출한 이름으로 저장
		imageDTO.setPath(fileName); //해당 이름을 DB로 전달하기 위해 저장
		imageService.insert(imageDTO); //DB에 추가

		//수정한 글 상세 페이지로 이동
		return "redirect:/boardInfo.do?bid="+boardDTO.getBid();
	}

 

저장한 이미지를 다시 출력할 때는 DB에서 저장한 파일명을 불러와 전달하기만하면 된다.

	@RequestMapping("/boardInfo.do")
	public String boardInfo(BoardDTO boardDTO, Model model, ImageDTO imageDTO) {
		ImageDTO image = imageService.selectOne(imageDTO);
		//만약 이미지가 존재한다면
		if(image != null) { //NPE방지
			//이미지 경로 전달
			model.addAttribute("imgPath", image.getPath());
		}
		model.addAttribute("data", boardService.selectOne(boardDTO));
		return "boardInfo";
	}

 

 

추가로 이미지 미리보기 JS

function preview(event) {
    console.log("js/preview.js");
    const file = event.target.files[0];
    const reader = new FileReader();
    reader.onload = function() {
        const imgPreview = document.getElementById('previewImage');
        imgPreview.src = reader.result;
        imgPreview.style.display = 'block';
    };
    if (file) {
        reader.readAsDataURL(file);
    }
}

 

 

코드를 정상적으로 입력했는데도 오류가 발생할 때 context.xml에 속성추가하는 법

 

[Error/Spring] MultipartException : Failed to parse multipart... (+ Intellij server context.xml 꺼내기)

Request processing failed: org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request  해당 에러는 여러가지 문제로 발생한다. 코드에 이상이 없다는 가정하에 해당 문제가 반복된다면

minibcake.tistory.com

 

 

Multipart 메서드

메서드 input output 설명
getName() 없음 String 파일 필드 이름을 반환
getOriginalFilename() 없음 String 업로드된 파일의 원본 이름 반환
getContentType() 없음 String 파일의 MIME 타입 반환 (예: image/jpeg, text/plain)
getSize() 없음 long 파일의 크기를 바이트 단위 반환
isEmpty() 없음 boolean 파일이 비어 있는지 여부 반환 (파일이 없거나 크기가 0일 때 true)
getBytes() 없음 byte[] 파일 내용을 바이트 배열 반환
getInputStream() 없음 InputStream 파일의 내용을 읽을 수 있는 InputStream 반환
transferTo(File dest) File void 업로드된 파일을 지정한 파일 경로 저장
transferTo(Path dest) Path void 업로드된 파일을 지정한 경로(Path) 저장

*mkdirs같은 기능은 없음 File사용