Multipart
checkMultipart
HTTP 요청이 멀티파트 요청인지 확인하고, 멀티파트 요청인 경우 이를 적절하게 처리하는 역할을 한다.
멀티파트 요청은 파일 업로드와 관련된 요청을 처리할 때 사용된다.
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
this.logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
}
} else if (hasMultipartException(request)) {
this.logger.debug("Multipart resolution previously failed for current request - skipping re-resolution for undisturbed error rendering");
} else {
try {
return this.multipartResolver.resolveMultipart(request);
} catch (MultipartException var3) {
if (request.getAttribute("jakarta.servlet.error.exception") == null) {
throw var3;
}
}
this.logger.debug("Multipart resolution failed for error dispatch", var3);
}
}
return request;
}
this.multipartResolver.resolveMultipart(request)는 멀티 파트로 변환하는 메서드로 별도의 multipartResolver를 설정하지 않으면 StandardServletMultipartResolver가 호출되고, StandardMultipartHttpServletRequest가 반한된다.
public class StandardServletMultipartResolver implements MultipartResolver {
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
}
StandardMultipartHttpServletRequest
StandardMultipartHttpServletRequest에 대해 간단하게 살펴보자.
request를 생성자로 받으면 parseRequest 메서드가 호출된며, 요청에 있는 parts들을 multipartFiles에 저장하게 된다.
multipartFiles는 상속받은 AbstractMultipartHttpServletRequest에 있는데, 다음과 같이 key-value 구조로 되어있는 것을 알 수 있다.
@Nullable
private MultiValueMap<String, MultipartFile> multipartFiles;
public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
@Nullable
private Set<String> multipartParameterNames;
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing) throws MultipartException {
super(request);
if (!lazyParsing) {
this.parseRequest(request);
}
}
private void parseRequest(HttpServletRequest request) {
try {
Collection<Part> parts = request.getParts();
this.multipartParameterNames = new LinkedHashSet(parts.size());
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap(parts.size());
Iterator var4 = parts.iterator();
while(var4.hasNext()) {
Part part = (Part)var4.next();
String headerValue = part.getHeader("Content-Disposition");
ContentDisposition disposition = ContentDisposition.parse(headerValue);
String filename = disposition.getFilename();
if (filename != null) {
files.add(part.getName(), new StandardMultipartFile(part, filename));
} else {
this.multipartParameterNames.add(part.getName());
}
}
this.setMultipartFiles(files);
} catch (Throwable var9) {
this.handleParseFailure(var9);
}
}
protected final void setMultipartFiles(MultiValueMap<String, MultipartFile> multipartFiles) {
this.multipartFiles = new LinkedMultiValueMap(Collections.unmodifiableMap(multipartFiles));
}
}
코드 중에 ContentDisposition이 있는데, ContentDisposition은 파일 다운로드 또는 파일 첨부와 같은 상황에서 Content-Disposition 헤더를 설정하거나 파싱할 때 사용되는 클래스로, 여기서는 filename을 얻기 위해 사용되었다.
cleanupMultipart
protected void cleanupMultipart(HttpServletRequest request) {
if (this.multipartResolver != null) {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
if (multipartRequest != null) {
this.multipartResolver.cleanupMultipart(multipartRequest);
}
}
}
cleanupMultipart는 multipartRequest가 있을 때, multipartResolver에게 cleanupMultipart를 요청한다.
해당 메서드에서는 요청이 AbstractMultipartHttpServletRequest 인스턴스인지를 확인하고, 저장되었던 part를 삭제하는 역할을 수행한다.
public void cleanupMultipart(MultipartHttpServletRequest request) {
if (request instanceof AbstractMultipartHttpServletRequest abstractMultipartHttpServletRequest) {
if (!abstractMultipartHttpServletRequest.isResolved()) {
return;
}
}
try {
Iterator var3 = request.getParts().iterator();
while(var3.hasNext()) {
Part part = (Part)var3.next();
if (request.getFile(part.getName()) != null) {
part.delete();
}
}
} catch (Throwable var5) {
LogFactory.getLog(this.getClass()).warn("Failed to perform cleanup of multipart items", var5);
}
}
Part
Part 인터페이스는 Java Servlet API에서 제공하는 인터페이스로, 멀티파트(form-data) 요청을 처리하기 위해 사용된다. 주로 파일 업로드와 관련된 기능을 제공하며, 클라이언트가 전송한 파일이나 데이터의 세부 정보를 다룰 때 사용하는 인터페이스이다.
Part의 구현체는 ApplicationPart와 MockPart가 있는데, test를 제외하고는 ApplicationPart를 사용하게 된다.
인터페이스를 보면 알 수 있듯이, 파일의 내용과 메타 데이터를 저장하는 것을 알 수 있다.
public interface Part {
InputStream getInputStream() throws IOException;
String getContentType();
String getName();
String getSubmittedFileName();
long getSize();
void write(String var1) throws IOException;
void delete() throws IOException;
String getHeader(String var1);
Collection<String> getHeaders(String var1);
Collection<String> getHeaderNames();
}
'Web > Spring' 카테고리의 다른 글
Spring은 비동기 처리를 한 후에 어떻게 응답할까? - 1 : 요청 쓰레드의 반환 (0) | 2024.10.25 |
---|---|
[Spring Web 6.1] DispatcherServlet 뜯어보기 - SSR 렌더링 과정 (0) | 2024.10.07 |
[Spring Web 6.1] DispatcherServlet 뜯어보기 - view 관련 (0) | 2024.10.07 |
[Spring Web 6.1] DispatcherServlet 뜯어보기 - dispatch (0) | 2024.10.07 |
[Spring Web 6.1] DispatcherServlet 뜯어보기 - getter & setter (0) | 2024.10.04 |