스프링 부트

[SpringBoot] ajax - multipart/form-data 로 요청하기!

h__hj 2022. 8. 14. 00:02

# ajax - multipart/form-data

 jQuery ajax 의 contentType이 multipart/form-data 일 때, FormData 와 MultipartFile 를 이용하여 file 데이터 맵핑하기!

# 환경

/**
 * tool: STS 4.13.0
 * version: 2.7.3-SNAPSHOT
 * java: 1.8
 * type: MAVEN
 * view: THYMELEAF
 * jQuery: 3.6.0
 */

# page

# html (일부분 발췌)

<h1>파일 테스트 페이지 입니다.</h1>
	
<h3>file one</h3>
<form id="formFileOne">
    <input type="text" id="text" name="text" placeholder="Text" style="display: inline;" required/>
    <br>
    <input type="file" id="data" name="file" placeholder="File" style="display: inline;"/>
    <br>
    <input type="submit" value="SUBMIT" style="display: inline;"/>
</form>
<br>
	
<h3>file list</h3>
<form id="formFileList">
    <input type="text" name="text" placeholder="Text" style="display: inline;" required/>
    <br>
    <input type="file" name="files" placeholder="File" style="display: inline;"/>
    <br>
    <input type="file" name="files" placeholder="File" style="display: inline;"/>
    <br>
    <input type="submit" value="SUBMIT" style="display: inline;"/>
</form>
<br>

# function (일부분 발췌)

$(document).ready(function() {
    $('#formFileOne').on('submit', function(event) {
        event.preventDefault();
        sendFileOne();
    })
    $('#formFileList').on('submit', function(event) {
        event.preventDefault();
        sendFileList();
    })
});
    
function sendFileOne() {
    const formData = new FormData($('#formFileOne')[0]);	
    sendFile(`/test/file/one`, formData,
        function(result) {
            console.log("RESULT:", result);
        }
    );
}
	
function sendFileList() {
    const formData = new FormData($('#formFileList')[0]);
    sendFile(`/test/file/list`, formData,
        function(result) {
            console.log("RESULT:", result);
        }
    );
}

# jQuery ajax (일부분 발췌)

function sendFile(url, formData, cbSuccess) {
    $.ajax({
        // options
            url         : url,
            method      : "POST",
            data        : formData,
            contentType : false,
            dataType    : "json", 
            processData : false,
        // success
        }).done(function(output, textStatus, jqXHR) {
            if(typeof cbSuccess === 'function') {
                cbSuccess(output);
            }
        // error	
        }).fail(function(jqXHR) {
            console.log("ERROR");
        // complete
        }).always(function(output, textStatus, jqXH) {
            console.log("COMPLETE");
        }
    );
}

# @Controller (일부분 발췌)

@Slf4j
@Controller
@RequestMapping("/test/file")
public class TestFileController {
	
	@PostMapping("/one")
	@ResponseBody
	public Map<String, Object> one(@RequestParam("file") MultipartFile file, @RequestParam("text") String text) {
		log.debug("[PARAMETER] {}, {}", text, file.getOriginalFilename());
		log.debug("[FILE] Name        : {}", file.getName());
		log.debug("[FILE] OriginalName: {}", file.getOriginalFilename());
		log.debug("[FILE] Size        : {} Byte", file.getSize());
		log.debug("[FILE] isEmpty     : {}", file.isEmpty());
		
		Map<String, Object> response = new HashMap<String, Object>();
		response.put("text", text);
		response.put("file", file.getOriginalFilename());
		return response;
	}
	
	@PostMapping("/list")
	@ResponseBody
	public Map<String, Object> two(@RequestParam("files") MultipartFile[] files, @RequestParam("text") String text) {
		log.debug("[PARAMETER] {}, {}", text, files.length);
		
		String[] fileNames = new String[files.length];
		
		for (int i = 0; i < files.length; i++) {
			String fileName = "";
			MultipartFile file = files[i];
			if(!file.isEmpty()) {
				log.debug("[FILE {}] Name        : {}", (i+1), file.getName());
				log.debug("[FILE {}] OriginalName: {}", (i+1), file.getOriginalFilename());
				log.debug("[FILE {}] Size        : {} Byte", (i+1), file.getSize());
				fileName = file.getOriginalFilename();
			}
			fileNames[i] = fileName;
		}
		
		Map<String, Object> response = new HashMap<String, Object>();
		response.put("text", text);
		response.put("files", fileNames);
		return response;
	}
	
}

# 내용

 html 에서 FormData 로 ajax 통신을 할 때 중요한 부분이 몇가지 있다.

  1. 해당 form 태그의 submit 기본 이벤트 정지.
    • event.preventDefault() 를 이용하여 submit의 기본동작을 금지시킨다.
  2. new FormData($(formElement)[0]);
    • formElement의 [0] 번지는 <form></form> 태그 자체 값이다.
  3. FormData.append()
    • append(name, value)는 수동으로 값을 설정할 때 사용한다.
    • form element 안 input 의 name이 설정 되어있다면 할 필요 없음.
    • form tag에서 이용하는 serialize를 사용할 필요없음.
  4. ajax options - contnetType: false
    • 컨텐트 타입을 헤더에 지정 안함.. file이면 자동으로 multipart/form-data; boundary=----WebKitFormBoundaryhGbwlJ7TC58D65R4 식으로 설정된다.
    • default: 'application/x-www-form-urlencoded; charset=UTF-8'
  5. ajax options - processData : false
    • 입력 데이터가 문자열이 아니라는 설정.
    • default: true

# ajax 요청 방식에 따른 파라미터 맵핑 방법.

https://hjho95.tistory.com/14 ajax settings
https://hjho95.tistory.com/7 application/x-www-form-urlencoded
https://hjho95.tistory.com/8 application/json
https://hjho95.tistory.com/9 form tag
https://hjho95.tistory.com/10 multipart/form-data
https://hjho95.tistory.com/12 PUT, PATCH, DELETE
https://hjho95.tistory.com/11 POST 방식으로 PUT, PATCH, DELETE 사용하기

# ajax 시리즈의 참조 페이지.

ajax settings: https://api.jquery.com/jquery.ajax/#jQuery-ajax-url-settings
preventDefault: https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault
MultipartFile: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html
FormData: https://developer.mozilla.org/ko/docs/Web/API/FormData