MultipartFile을 활용한 파일 업로드

이죠·2024년 11월 27일

spring

목록 보기
1/2

개요

스프링 부트 환경에서 바이너리 형식 파일을 업로드 하기 💾

Springboot 3.4.0, JDK 21SE, Spring data JPA, mariaDB, lombok, modelmapper




🎈목차

    1. MultipartFile 이란?

    2. Method Summary

    3. <form> 사용을 통한 파일 업로드 예제

    4. 비동기$(Ajax)를 통한 파일 업로드 예제

    5. 결론


🌵 1. MultipartFile 이란?

MultipartFile 이란 SpringFramework에서 제공하는 파일 업로드용 인터페이스이다.
보통 HTML 환경에서 유저Multipart 형식의 데이터를 DB 에 저장을 할때 사용한다.
SpringFramework 내 Spring Web 모듈에서 제공하는 인터페이스로 지금부터 사용법을 알아보자!

❕ Multipart 타입이란?

MIME 타입 중 multipart/form-data 타입을 사용하여 
한 HTTP 요청 안에서 여러 가지 데이터를 분리된 "part"로 전송하는 방법이다.
예를 들어 텍스트 데이터와 바이너리 형식의 데이터들(이미지 파일 등)을 하나의 요청으로 보낼때 사용한다.

아파치 톰캣 폴더 내의 web.xml 파일 내 정어 있는 밈타입들을 확인해보면 이해하기 쉬움

 

🌵 2. Method Summary

MultipartFile에서 제공하는 Method들을 알아보자.

    1. bolean isEmpty()
    	- 파일이 비어있는지 확인하여 boolean 타입으로 반환합니다.
    2. byte[] getBytes()
    	- 파일의 내용을 byte형식의 배열로 반환합니다.
	3. String getContentType()
    	- 파일의 콘텐츠 유형을 MIME_(인터넷에 전달되는 파일 포맷 및 식별자)_타입으로 반환
        - type/subtype 형식으로 반환 ex) application/json, image/png
    4. InputStream getInputStream()
    	- 파일 내용을 읽기 위해 InputStream을 반환(read, available등의 메소드 사용이 필요 할 때)
    5. String getName()
    	- 필드의 이름을 반환(예: <input id="1" name="potatoImg">의 name 부분)
    6. String getOriginalFilename()✨
    	- 파일의 원본 이름(클라이언트 파일 시스템 기준)을 반환
    7. Long getSize()
    	- 파일을 바이트 단위로 반환
    8. void transferTo(File dest 혹은 Path dest)✨
    	- 현재 MultipartFile에서 사용중인 임시 저장소에 있는 해당 파일을 지정된 dest 경로로 이동합니다.

 

🌵 3.<form> 방식

스프링 부트 환경에서 MultipartFile을 사용하여 form/post 방식으로 DB, Server local 스토리지에 파일을 저장해보자💾

### html 부분
    <form action="/potato/register" method="post" enctype="multipart/form-data">
        <p>
            감자 이름 : <input type="text" name="potatoName">
        </p>
        <p>
            감자 개월수 : <input type="number" name="potatoAge">
        </p>
        <p>
            감자 사진 : <input type="file" name="potatoImg">
        </p>
        <button type="submit">제출</button>
    </form>

html을 살펴보면
<form> 태그 내 enctype 속성을 꼭 "multipart/form-data"로 설정하여 사용하여야 한다.
만약 속성을 설정해주지 않을 경우 default로 application/x-www-form-urlencoded가 설정되어 바이너리 타입의 데이터를 수신하지 못하게 된다!

<input type="file">로 input을 추가하여
submit 시 컨트롤러로 문제 없이 파일이 갈 수 있도록 작성해준다.

컨트롤러 부분

 @PostMapping("/register")
    public String register(MultipartFile potatoImg, PotatoDTO potatoDTO){
        potatoService.imgRegister(potatoImg,potatoService.register(potatoDTO)); 
        // 글 등록 후 새로 생성된 글의 pk값을 리턴하여 해당 값을 상속받는 potatoImg에 전달
        return "/potato/success";
    }
	

컨트롤러 부분은 위와 같이 MultipartFile 인터페이스를 import✨하여 file 객체를 하나 파라미터로 받아주며 이외에 multipart/form-data로 함께 넘어온 나머지 값들은 DTO로 받아준다.
추가적으로 예제엔 imgRegister의 파라미터로 파일과 register의 리턴값인 Long을 같이 넣어주어 한줄로 작성했지만 가독성 및 유지보수를 위해 나눠서 관리 하시길 권장 드린다

서비스 부분(DB에 파일 저장)

	@Value("C:\\images")
	private String uploadPath;
    
@Override
    public void imgRegister(MultipartFile potatoImg, Long kno){ 
    // register method에서 리턴 받은 게시글(potato)의 키값을 받아 넣음
        try {
            String afterName = fileUplaod(potatoImg);
            // fileupload 메소드의 리턴값으로 UUID가 합쳐진 이름을 받아 풀 네임을 변수에 담는다 
            Potato potatoEntity = potatoRepository.findById(kno).get();
            // Join 컬럼에 값을 대입해주기 위해
            PotatoImgDTO potatoImgDTO =
                    PotatoImgDTO.builder()
                            .url(uploadPath+"/"+afterName)
                            .afterName(afterName)
                            .originalName(potatoImg.getOriginalFilename())
                            .potato(potatoEntity).build();
                      		//빌더 패턴을 통해 각 값들을 세팅                       
          	potatoImgRepository.save(modelMapper.map(potatoImgDTO, potatoImg.class));
        }
        catch (IOException ioException){
            System.out.println("파일 저장 오류");
        }

    }
	

DB에 파일을 저장하는 메소드로 Bulider 패턴을 통해 각각 필요한 컬럼 값들을 세팅해준다.
그 중 눈여겨 볼 내용은 .getOriginalFilename()✨으로 원본 이름을 가져와 저장하고
중복값 방지 등을 위한 방법으로 UUID를 앞에 붙혀 afterName도 붙혀준다.
url 부분의 파라미터 uploadPath는 서비스 상단에 경로를 변수에 담아 관리해주면 편하다.
※ 추가적으로 IE, Edge의 경우 파일이 경로명까지 붙어 들어오는 경우가 있어 lastIndex로 잘라서 이름을 겟하는 내용이 필요하다.

서비스 부분(local에 파일 저장)

    @Override
    public String fileUplaod(MultipartFile file) throws IOException {
        UUID uuid = UUID.randomUUID(); // java.util의 UUID를 이용하여 랜덤 UUID를 파일명 앞에 붙혀준다.
        String afterName = uuid.toString()+file.getOriginalFilename();
        // 파일 최종 이름에 랜덤 UUID를 붙혀 중복성을 제거하여 관리한다.
        String fileUploadUrl = uploadPath + "/" + afterName;
        // 업로드할 위치에 디렉토리 path+/+파일 이름을 넣어준다.
        File uploadFile = new File(fileUploadUrl);
        // 업로드할 파일을 만들어준다.
        try {
            file.transferTo(uploadFile);
            // MultipartFile을 해당 업로드할 파일 경로로 옮긴다.
		} catch (IOException ioException) {
            System.out.println("IOException 발생 : " + ioException.getMessage());
        }//IO예외처리
        return afterName; // 체인메소드 형식으로 위에서 활용하고자 리턴값 줌
    }

Local 경로에 실제 파일을 저장 하려면 위에서 배운 transferTo를 사용 할 것이다.
Filecopy 혹은 fileoutstream등 방법은 많지만 transferTo메소드를 사용하면 간편히 저장 할 수 있다.
위에서 파일 경로와 UUID를 붙힌 이름을 받아 새로운 File을 만들어주고
transferTo 파라미터✨로 넣어주면 끝이다!


 

🌵 4. 비동기$(Ajax) 방식

스프링 부트 환경에서 MultipartFile을 사용하여 JQuery Ajax 방식으로 DB, Server local 스토리지에 파일을 저장해보자💾

Controller 이후 데이터 DB 저장, 파일 저장 같은 경우 같은 경우 같은 로직으로 처리되기 때문에
생략 후 HTML과 Script, Controller 부분만 다룰 예정이다.
    <div class = 'uploadDiv'>
        <input id="uploadFile" type="file" name="uploadFile">
    </div>
    <button id="uploadBtn">UPLOAD</button>
<script>

        $("#uploadBtn").on("click",function(e){
            const uploadFile = $("#uploadFile")[0].files[0];
            const formData = new FormData()
            formData.append("uploadFile",uploadFile)
            console.log(formData)
            $.ajax({
                url:'/potato/upload',
                processData: false,
                contentType : false,
                enctype :"multipart/form-data",
                data : formData,
                type : 'POST',
                success : function(result){
                    alert("Good uploaded")
                    location.reload()
                },
                error : function (){
                    alert("failed, go work")
                    location.reload()
                }



            })
        })
    </script>

form을 사용하는 예제와 크게 다를게 없으나 Ajax로 보낼 경우
$(선택자)[0].files[0] 와 같이 JQuery 객체를 DOM 객체로 변환하여 담아야 함.

🔧Ajax 설정값

ajax 속성설정값
urlcontroller 부분의 포스트 맵핑 부분과 requestmapping을 고려하여 맵핑
processDatatrue일 경우 jQuery에서 데이터를 쿼리 문자열로 변환하므로 false로 설정
contentTypetrue일 경우 application/x-www-form-ulencoded로 인코딩하므로 false로 설정
enctypeform과 같이 multipart/form-data✨
data서버로 보낼 FormData 타입으로 생성한 객체
@ResponseBody
    @PostMapping("/upload")
    public void upload(@RequestParam("uploadFile") MultipartFile uploadFile){
        log.info(uploadFile.getOriginalFilename());
    }

같은 로직을 가지기에 log4j2로 들어오는지만 찍어 보았다.
@restconroller를 사용 할 경우 @ResponseBody는 생략 가능하나 일반 Controller이기에 사용
@ReuqestParam을 통해 맵핑하여 파일 수신 완료

🌵5. 결론

MultipartFile을 이용하여 MVC 패턴 중 뷰에서 모델로 파일을 보내는 방법을 알아보았다.
유의 해야 할 점은 form으로 보낼 경우 encType 설정이 잘 되었는지 확인,
Ajax를 통해 보낼 경우 각 종 설정값이 잘 설정되었는지 확인이 필요한 것 같다.
파일이름 중복 등의 예외사항 및 파일 관리를 고려하여
날짜별 로컬 서버 dir 관리 등의 방법도 필요 할 것으로 보인다.

다음장(CRUD 중 RUD 작성 예정)에서 만나요😁

profile
윈도우 페인터의 이중 생활

3개의 댓글

comment-user-thumbnail
2024년 11월 27일

큰 도움이 되는 정보 감사합니다~ 굿~!

1개의 답글
comment-user-thumbnail
2025년 1월 28일

다음 글 언제 나오나요 !

답글 달기