Languages/java

[JAVA] fileUpload03

뱅타 2021. 4. 15. 23:04 💻 읽는시간 : 7 분

FileUpload 중에 part라는 API를 고쳐서 사용해 보기.( AdapterPatter 적용)

우선 파일 업로드 시 주의사항

  1. file은 2진데이터(byte)타입이다.(get사용불가)

    2진데이터 → post

  1. file과 text 등 다른 데이터들을 받으려면 enctype을 설정해야한다.(multipart/form-data)

    다른타입의 데이터를 입력 → multipart

  1. part API를 사용하기 위해 servlet의 버전을 맞춰 주어야 한다.

    part 사용하기 (버전 맞춰주기) → web.xml( multipart-config)

web.xml에 등록되어 있는 Front Servlet에 multipart-config 설정을 집어넣어준다.

이렇게 파트를 불러와서 사용가능.

파일 업로드 시 순서

  1. 어디에다가 저장

    웹 리소스, 클래스패스, 파일시스템으로 저장 가능.

  1. 메타데이터를 어떻게 뽑을것인가
  1. 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이란 클래스 생성.

파일 업로드 시 주의할 점.

  1. 확장자 제거
  1. 업로드하는 사람이 최종적으로 저장되는 파일의 이름을 예측하지 못하게 하기위해 랜덤한 파일명 부여.

** 참고 kisa에 있는 보안ppt를 한번읽어보면 좋다.

https://www.kisa.or.kr/public/laws/laws3_View.jsp?cPage=6&mode=view&p_No=259&b_No=259&d_No=88&ST=T&SV=

실제 파일 저장 시 확장자와 이름을 바꿔서 저장한다!

클라이언트가 서버에 올라갈 파일의 확장자와 이름을 알게 된다면 다른 파일을 변경시킨다거나 훼손시킬 수도 있기 때문!(이라고 하는데 상상이 잘 안간다)

이름 뿐만 아니라 이 녀석의 전체 경로를 달라는 것.

파일을 저장할 수 있는 함수 생성!

이러한 상황을 대비해서 리스트로

  • 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이 같은 여러객체를 받기 위해 배열로!

728x90
반응형