파일을 선택 할 때는, 일반적인 text 구조가 아니기 때문에 type="file"을 사용한다.
파일이 이름이 어떤 것인지(=meta data)만 넘겨진다.
Body 영역 안에 문자열이 아닌 다른 것이 들어가야 파일이 업로드 될 것이다. Content-Type이 이렇게 설정되어 있어서 문자열로 넘어간다.
기본적으로 설정되어있기 때문에 쿼리 스트링을 문자열 타입으로 넘길 수 있었다.
파일 업로드는 이진데이터로 넘겨야 하기 때문에 Body안에 문자열/이진데이터 둘 다 넘어갈 수 있는 구조가 되어야 한다. 때문에 첫 번째 마임을 Multi-part/form 데이터로 설정하고 Form 태그 안의 input type에 따라 3가지로 나눠서 보내야 한다.
Body안에 있는 form 태그안에 넣어서 보내줄 수 있는 구조로 사용해야 한다.
enctype="multipart/form-data
경계선 문자를 기준으로 해서 영역이 3군데로 나눈 것을 확인이 가능하다.
이 안에 문자는 없고, 대신 Part만 존재한다. 아무런 데이터를 입력하지 않았기 때문에 아무것도 뜨지 않는 모습이다.
파라미터는 존재하지 않고 part만 존재하기 때문에 String param을 받을 수가 없다.
String textParam = req.getParameter("textParam");
String numberParam = req.getParameter("numberParam");
getPart()
: multi-part/form-data 형식으로 넘어 오는 것을 잡을 수 있다.
Throws:
IllegalStateException - if the request body is larger than maxRequestSize, or any Part in the request is larger than maxFileSize, or there is no @MultipartConfig or multipart-config indeployment descriptors
Chunk
: 클라이언트에서 서버로 데이터를 보낼 때 데이터를 쪼개서 보내는 것. 쪼개진 데이터들을 하나씩 임시 저장해두는 곳을 location
mkdirs() -> 폴더의 계층 구조까지 모두 만들어준다.
multipart 안에 들어있는 header를 꺼내서 파일 명을 가져온다.
이미지라는 마임만을 가진 파일만 선택을 할 수 있도록 accept를 설정해준다.
하지만 클라이언트 사이드 검증은 클라이언트가 얼마든지 조작할 수 있으므로 서버사이드에서도 검증을 해줘야 한다.
String mime = filePart.getContentType(); // ===> 현재 파트 하나에 대한 마임을 가지고 이다.
/**
* 서버 사이드 마임 검증
*/
if(mime !=null || !mime.startsWith("image/")) { // 이미지가 아닌 잘못된 요청
resp.sendError(400);
return;
}
저장할 때 파일 명에 원본 파일 명으로 저장하지 않으면, 공격할 해커는 어떤 이름으로 파일이 저장되었는지 모르기 때문에 공격이 막힌다. (SaveFIleName을 쓰지 않고, 전혀 예상할 수 없는 새로운 파일 명을 사용하는 것이다.)
원본 파일 명을 넣었을 때, 여러명의 클라이언트가 동일한 파일 명을 사용할 수 있기 때문에 새로운 파일 명을 사용한다.
가장 좋은 방법은, 이름 + 현재 시간을 포함시킨다. 또는, 유효 아이디를 사용한다.
어플리케이션의 핵심적인 Business Logic을 가지고 있으면 안됨 (=Optional 기능이기 때문이다..)
FrontController 앞단에 위치한다. 필터는 한가지가 아니라 여러가지 사용될 수 있기 때문에, Filter Chain의 형태로 관리할 수 있게 된다.
필터를 사용하는 주요 목적
- 요청에 대한 전처리 과정. (요청이 가진 일부 성질을 변경하고 싶을 때 사용한다.)
- 보안을 목적으로 접근 제어를 하고 싶을 때 사용한다.
- 요청/응답 데이터가 필터링 되는 순서가 역순. 응답데이터의 후처리를 담당하기 위해서 이다.
ex) 응답 데이터가 나가야 하는데, 1GB 이라서 사이즈를 줄여야 할 때 압축을 해야 한다. 압축하는 작업을 Filter에서만 전문적으로 하게 되면, 뒷 단에서는 크기를 고려하지 않아도 된다.
기존의 로직을 변경하지 않으면서 어플리케이션을 풍부하게 만들고 싶을 때 사용한다.
1.Authentication Filters
2.Logging and Auditing Filters
3.Image conversion Filters
4.Data compression Filters
5.Encryption Filters
6.Tokenizing Filters
Token : 문장을 구성하는 최소 단위
7.Filters that trigger resource access events
8.XSL/T filters
9.Mime-type chain Filter
Filter를 구현하고 나면 WAS에 등록한다 -> LifeCycle을 WAS가 관리한다. Servlet과 똑같이 CallBack Method가 존재한다.
// 요청 하나하나를 담당하는 요청 콜백
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
}
FilterChain chain
을 통해서 어떤 필터인지 경계선을 그어준다.
필터는 리스너와 서블릿 사이에 구현한다.
어떤 경우에서든 응답데이터가 나갈 수 있는 chain.doFilter를 사용해서 권한을 넘겨줘야 한다. 넘겨주지 않을 시에는 빈 사이트가 나온다.
Part Wrapper를 사용해서 request 원본 요청의 성질을 변경하려고 한다. 하지만, request를 건드릴 수 없기 때문에 Wrapper를 대신 만들어 주는 것이다.