FileUpload 중에 part라는 API를 고쳐서 사용해 보기.( AdapterPatter 적용)
우선 파일 업로드 시 주의사항
- file은 2진데이터(byte)타입이다.(get사용불가)
2진데이터 → post
- file과 text 등 다른 데이터들을 받으려면 enctype을 설정해야한다.(multipart/form-data)
다른타입의 데이터를 입력 → multipart
- part API를 사용하기 위해 servlet의 버전을 맞춰 주어야 한다.
part 사용하기 (버전 맞춰주기) → web.xml( multipart-config)
web.xml에 등록되어 있는 Front Servlet에 multipart-config 설정을 집어넣어준다.
이렇게 파트를 불러와서 사용가능.
파일 업로드 시 순서
- 어디에다가 저장
웹 리소스, 클래스패스, 파일시스템으로 저장 가능.
- 메타데이터를 어떻게 뽑을것인가
- db에다 저장하기
Adapter역할을 할 녀석은 파라미터로 반드시 Adaptee를 받을 수 있어야 한다.
HttpServletRequsetWrapper를 상속받으면 기본적으로 파라미터를 받는 생성자를 만들라고 한다.
MultipartHttpServletRequest 파일 코드
package kr.or.ddit.mvc.filter.wrapper; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.Part; // 원본 request에 기능들을 추가하고 있음. public class MultipartHttpServletRequest extends HttpServletRequestWrapper{ // private Map<String, part> private Map<String, List<MultipartFile>> fileMap; // 모든 데이터는 request가 가지고 있다. public MultipartHttpServletRequest(HttpServletRequest request) throws IOException, ServletException { super(request); fileMap = new LinkedHashMap<>(); parseRequest(request); } private void parseRequest(HttpServletRequest request) throws IOException, ServletException { Collection<Part> parts = request.getParts(); for(Part tmp : parts) { if(tmp.getContentType()== null) continue; String partName = tmp.getName(); List<MultipartFile> list = fileMap.get(partName); if(list == null) { list = new ArrayList<>(); fileMap.put(partName, list); } list.add(new MultipartFile(tmp)); } } public Map<String, List<MultipartFile>> getFileMap() { return fileMap; } private MultipartFile getFile(String name) { List<MultipartFile> files = fileMap.get(name); MultipartFile file = null; if(files!=null && files.size() > 0) file = files.get(0); return file; } // 하나의 객체를 multipartFile로 public List<MultipartFile> getFiles(String name) { return fileMap.get(name); } }
이렇게 감싸서 보내면 변경된 request(req → wrapper)가 보내지게 된다.
필터가 해야할 일.
- 파일이 포함된 multipart 요청인지 식별.
- multipart 요청이라면, 원본 요청을 wrapper로 변경.
- wrapper를 이용해 Part 데이터를 쉽게 핸들링할 수 있는 구조 설정.
파트가 가지고 있지 않은 메서드를 임의로 추가해서 새로운 어뎁터 만들어 보기.
MultipartFile이란 클래스 생성.
파일 업로드 시 주의할 점.
- 확장자 제거
- 업로드하는 사람이 최종적으로 저장되는 파일의 이름을 예측하지 못하게 하기위해 랜덤한 파일명 부여.
** 참고 kisa에 있는 보안ppt를 한번읽어보면 좋다.
실제 파일 저장 시 확장자와 이름을 바꿔서 저장한다!
클라이언트가 서버에 올라갈 파일의 확장자와 이름을 알게 된다면 다른 파일을 변경시킨다거나 훼손시킬 수도 있기 때문!(이라고 하는데 상상이 잘 안간다)
이름 뿐만 아니라 이 녀석의 전체 경로를 달라는 것.
파일을 저장할 수 있는 함수 생성!
이러한 상황을 대비해서 리스트로
- name이 같은 input태그들이 존재할 때 집합객체로 받아야 한다.
adaptee가 가지고 있지 않은 것들을 adapter가 완성시킴.
adapter
package kr.or.ddit.mvc.filter.wrapper; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.Part; // 원본 request에 기능들을 추가하고 있음. public class MultipartHttpServletRequest extends HttpServletRequestWrapper{ // private Map<String, part> private Map<String, List<MultipartFile>> fileMap; // 모든 데이터는 request가 가지고 있다. public MultipartHttpServletRequest(HttpServletRequest request) throws IOException, ServletException { super(request); fileMap = new LinkedHashMap<>(); parseRequest(request); } private void parseRequest(HttpServletRequest request) throws IOException, ServletException { Collection<Part> parts = request.getParts(); for(Part tmp : parts) { if(tmp.getContentType()== null) continue; String partName = tmp.getName(); List<MultipartFile> list = fileMap.get(partName); if(list == null) { list = new ArrayList<>(); fileMap.put(partName, list); } list.add(new MultipartFile(tmp)); } } public Map<String, List<MultipartFile>> getFileMap() { return fileMap; } private MultipartFile getFile(String name) { List<MultipartFile> files = fileMap.get(name); MultipartFile file = null; if(files!=null && files.size() > 0) file = files.get(0); return file; } // 하나의 객체를 multipartFile로 private List<MultipartFile> getFiles(String name) { return fileMap.get(name); } }
adaptee
package kr.or.ddit.mvc.filter.wrapper; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.UUID; import javax.servlet.http.Part; import org.apache.commons.lang3.StringUtils; public class MultipartFile { private Part adaptee; private String originalFilename; // 해킹을 방지하기 위해 랜덤한 이름을 설정하자. private String uniqueSaveName; private boolean empty; public MultipartFile(Part adaptee) { super(); this.adaptee = adaptee; String disposition = adaptee.getHeader("Content-Disposition"); int index = disposition.indexOf("\"", disposition.indexOf("filename=")); if(index!=-1) { originalFilename = disposition.substring(index).replace("\"", ""); } empty = StringUtils.isBlank(originalFilename); this.uniqueSaveName = UUID.randomUUID().toString(); } private String getOriginalFilename(Part part) { // Content-Disposition: form-data; name="uploadFile1"; filename="test.jpg" String disposition = part.getHeader("Content-Disposition"); int index = disposition.indexOf("\"", disposition.indexOf("filename=")); String originalFilename = null; if(index!=-1) { originalFilename = disposition.substring(index).replace("\"", ""); } return originalFilename; } public String getName() { return adaptee.getName(); } public String getContentType() { return adaptee.getContentType(); } public long getFileSize() { return adaptee.getSize(); } // 기존의 part가 가지고 있지 않은 메서드 추가하기 public String getOriginalFilename() { return originalFilename; } public String getUniqueSaveName() { return uniqueSaveName; } public InputStream getInputStream() throws IOException { return adaptee.getInputStream(); } public void saveTo(File saveFolder) throws IOException { File saveFile = new File(saveFolder, uniqueSaveName); adaptee.write(saveFile.getAbsolutePath()); } public boolean isEmpty() { return empty; } }
part만을 가지고 사용한 파일을 어뎁터를 이용해서 만들어보기
part(adaptee) → multipart(adapter)
어노테이션 변경시키기.
컨트롤러의 책임을 줄이기 위해서.
name이 같은 여러객체를 받기 위해 배열로!
Uploaded by Notion2Tistory v1.1.0