해당 내용은 POST 방식만을 고려한 내용이다.
Multipart/form-data의 HTTP 간단한 구조 알기
이미지를 업로드를 할 때 이미지만 업로드하는 것이 아닌 부가적인 정보들도 같이 업로드 해야하는 경우가 많다.
헤더는 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW와 같이
요청을 하고, boundary로 각각의 정보를 구분하여 전송하게 된다.
HTTP Reqeust
POST /urls/url/test
Host: localhost:8080
Cache-Control: no-cache
Postman-Token: f15dcf2b-ffdf-6e53-3411-a7bf27226668
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="user_name"
"monkey"
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="image"; filename="스크린샷 2022-11-26 오후 6.58.50.png"
Content-Type: image/png
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="banner"; filename="짱구 이미지.png"
Content-Type: image/png
------WebKitFormBoundary7MA4YWxkTrZu0gW--
간단하게 설명하자면,
Content-Type을 multipart/form-data
: form-data로 multipart를 보내겠다는 의미이다.bonudary
: 랜덤으로 브라우저가 생성하는 값이다. 이 값으로 강 form-data들을 구분한다.name
: 파라미터의 이름을 담는 필드이다. 현재 여기서 보낸 필드명은user_name
이다.
user_name의 값은 단순한 텍스트이다.
값은 monkey이다.image/png
: user_name과 다르게 파일은 Content-Type이 image/png이다.- 마지막 코드에서 boundary 마지막에
‘--’
이 붙어있는데 이건 boundary의 마지막을 의미한다.
MultiPart만 받는 방법
@RequestParam
@RequestParam(value = "imageOne") MultipartFile image1,
@RequestParam(value = "imageTwo", required = false) MultipartFile image2
@RequestPart
@RequestPart(value = "imageOne") MultipartFile image1,
@RequestPart(value = "imageTwo", required = false) MultipartFile image2
애노테이션 사용하지 않기
MultipartFile image1,
MultipartFile image2
위 두 방법의 차이점
- @RequestPart와 @RequestParam은
reuquire = true
가 default이기 때문에 필수로 파라미터를 넘겨주지 않으면Required request part 'imageTwo' is not present
에러가 발생한다.require = false
로 해결할 수 있다.
파일이 없어도 동일하다. 이유는 파일이 없으면 Conten-Type도 없기 때문이다. Content-Disposition: form-data; name="imageTwo"; filename="" Content-Type:
- 반면 애노테이션을 사용하지 않으면 그냥 null로 넘어온다.
공통점은
require = false
일 경우 파라미터를 넘기지 않으면 null로 들어온다.
@RequestParam, @RequstPart의 차이점
@RequestParam이란?
Annotation which indicates that a method parameter should be bound to a web request parameter.
Supported for annotated handler methods in Spring MVC and Spring WebFlux as follows:
- In Spring MVC, "request parameters" map to query parameters, form data, and parts in multipart requests. This is because the Servlet API combines query parameters and form data into a single map called "parameters", and that includes automatic parsing of the request body.
- In Spring WebFlux, "request parameters" map to query parameters only. To work with all 3, query, form data, and multipart data, you can use data binding to a command object annotated with
[ModelAttribute](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html)
.If the method parameter type is
[Map](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html)
and a request parameter name is specified, then the request parameter value is converted to a[Map](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html)
assuming an appropriate conversion strategy is available.If the method parameter is
[Map<String, String>](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html)
or[MultiValueMap<String, String>](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/MultiValueMap.html)
and a parameter name is not specified, then the map parameter is populated with all request parameter names and values.
- request parameter, mulitpart를 지원한다.
@RequestPart란?
Annotation that can be used to associate the part of a "multipart/form-data" request with a method argument.
Supported method argument types include
[MultipartFile](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartFile.html)
in conjunction with Spring's[MultipartResolver](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/multipart/MultipartResolver.html)
abstraction,jakarta.servlet.http.Part
in conjunction with Servlet multipart requests, or otherwise for any other method argument, the content of the part is passed through an[HttpMessageConverter](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html)
taking into consideration the 'Content-Type' header of the request part. This is analogous to what @[RequestBody](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html)
does to resolve an argument based on the content of a non-multipart regular request.Note that @
[RequestParam](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html)
annotation can also be used to associate the part of a "multipart/form-data" request with a method argument supporting the same method argument types. The main difference is that when the method argument is not a String or rawMultipartFile
/Part
,@RequestParam
relies on type conversion via a registered[Converter](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/convert/converter/Converter.html)
or[PropertyEditor](https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/java/beans/PropertyEditor.html)
while[RequestPart](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestPart.html)
relies on[HttpMessageConverters](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html)
taking into consideration the 'Content-Type' header of the request part.[RequestParam](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html)
is likely to be used with name-value form fields while[RequestPart](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestPart.html)
is likely to be used with parts containing more complex content e.g. JSON, XML).
- MultiPart를 지원한다.
- @RequeestParam은 name-value를 지원하지만, @RequestPart는 보다 복잡한 JSON, XML등과 같은 것들을 지원한다.
- 요청 헤더의 Content-Type에 해당하는 HttpMessageConverter를 사용한다.
차이점
@RequstParam
- query parameter, form data, Mumltipart 등 많은 요청 파라미터를 지원한다.
- 메소드 파라미터 타입이 String 또는 MultipartFile/Part가 아닌 경우 Converter 또는 PropertyEditor를 참조해 변환을 시도한다.
@RequestPart
- MultiPart를 지원한다.
- @RequeestParam은 name-value를 지원하지만, @RequestPart는 보다 복잡한 JSON, XML등과 같은 것들을 지원한다.
- 요청 헤더의 Content-Type에 해당하는 HttpMessageConverter를 사용한다.
qeury parameter와 File 같이 받기
사용이 불가한 경우
Content type 'application/octet-stream' not supported
발생
- @RequestPart만 사용
- MultiPart에 @RequestParam, queryParameter에 @RequestPart
사용 가능한 경우
- @RequestParam만 사용
- queryParameter에 @RequestParam, Multipart에 @RequestPart
DTO로 snake case → camel case로 받을 수 없을까?
POST 방식이고, body로 오는 json content-type이면, jackson의 @JsonProperty로 쉽게 해결이 가능하다.
하지만 GET 방식의 query string이거나, form-data, multipart이면 지원하지 않는다.
- case가 같을 경우 GET, POST 모두 @ModelAttribute 지원함.
- case가 다를 경우
- POST json body이면 jackson @JsonProperty로 변환 가능
- GET query string 안됨.
- form-data, multipart는 안됨
- form-data에 value를 json 형태로 넘기면 value자체는 @JsonProperty로 변환 가능
즉 key는 변환
- form-data에 value를 json 형태로 넘기면 value자체는 @JsonProperty로 변환 가능
@RequestParam을 사용하면 하나씩 컨트롤로에 필드를 지정해야 하기 때문에 불편하다.
현재 나의 상황은 multipart이면서 query string인 상황이기 때문에 이 부분에 대해서 설명한다.
시도중- String만 들어옴. image는 안들어옴.
@RequestParam Map<String, Object> params
) throws IOException {
ObjectMapper mapper = new ObjectMapper();
MallImageCreateRequest user = mapper.convertValue(params, MallImageCreateRequest.class);
참고
'Spring' 카테고리의 다른 글
Spring Cloud Stream으로 RabbitMQ 샘플 구축하기 (0) | 2023.04.01 |
---|---|
@JsonFromat, @DateTimeForamt이 계속 헷갈린다! (0) | 2022.11.19 |
Redis 동시성 이슈 개선하기 (0) | 2022.05.14 |
스프링 부트 Logger 사용법 및 팁 (0) | 2022.05.11 |
스프링 순환 참조와 생성자 주입을 사용해야 하는 이유 (0) | 2022.04.20 |