앞서 한 것들에게는
검증이 제대로 되어있지 않다.
- 저장하려는 폴더가 없다면 어떻게 할 것인가
- 실제로 이미지만 올라갈까?
- 널체크
- 버전을 타고있다.
jsp에서는 이렇게 작성했지만 실제로 넘어가는 값이 image만 있지 않을 가능성이 높다.
이러한 문제들을 한번 잡아보자.
1, 폴더가 없을 시
mkdirs()로 servletContext로 구한 경로의 모든 폴더들을 생성한다.
계층구조의 모든 폴더 생성.
이렇게 실제 폴더가 없을 시 폴더를 만들어 줌으로써 1번 해결.
2. 실제 이미지인가?
jsp에서는 image만 올리게끔 했지만 image가 올라오지 않을 수도 있다.
검증을 해주지 않을 시 오류가 뜨면 서버가 꺼질 수 있다.
타입을 가져와서 image/로 시작되는지 체크
mimetype을 check하고 오류를 상위로 올려준다.
실제 나는 오류를 handlerAdapter에서 잡아준다. (front , command pattern 참고.)
이렇게 작업해 주었기 때문에 해당 서버사이드에서 resp.senderror로 error를 처리 할 필요가 없다.
이렇게 이미지 파일을 검증한다.
3. 널체크하기
이렇게 null check
올라오는 파일이 두개. 그런데 각각 널체크를 하면 두번 해야하기 때문에
processPart 메소드를 생성해서 사용함.
4. 3.1버전에서만 쓸 수 있다.
getSubmittedFileName(); 메서드가 3.1 이상만 쓸 수 있다.
여기 보면
그러니 직접 파싱해 보자.
Content-Disposition을 부분을 들고와서 전처리를 해준다.
이렇게 파싱하고
맨 위에 녀석은 String타입이기 때문에 건너뛰게끔 하자. 오류난다.
이렇게 불러와서 쓰면 된다.
코드
package kr.or.ddit.fileupload.controller; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.http.Part; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import kr.or.ddit.mvc.annotation.Controller; import kr.or.ddit.mvc.annotation.RequestMapping; import kr.or.ddit.mvc.annotation.RequestMethod; import kr.or.ddit.mvc.annotation.resolvers.BadRequestException; import kr.or.ddit.mvc.annotation.resolvers.RequestParam; @Controller public class FileUploadController { private static final Logger logger = LoggerFactory.getLogger(FileUploadController.class); @RequestMapping("/fileUpload.do") public String form() { return "fileupload/uploadForm"; } @RequestMapping(value="/fileUpload.do", method=RequestMethod.POST) public String upload( @RequestParam("uploader") String uploader, HttpServletRequest req, HttpSession session) throws IOException, ServletException { ServletContext application = req.getServletContext(); String saveFolderUrl = "/prodImages"; File saveFolder = new File(application.getRealPath(saveFolderUrl)); if(!saveFolder.exists()) { saveFolder.mkdirs(); } Collection<Part> parts = req.getParts(); int index = 1; for(Part tmp : parts) { // 일반 파일이면(텍스트) 건너뛰어야한다. if(tmp.getContentType()==null) { continue; } String saveFileName = processPart(tmp, saveFolder); String saveFileUrl = saveFolderUrl+"/"+saveFileName; session.setAttribute("uploadFile"+index++, saveFileUrl); logger.info("saveFile : {}", saveFileUrl); } logger.info("uploader : {}", uploader); return "redirect:/fileUpload.do"; } 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; } private String processPart(Part uploadFile1, File saveFolder) throws IOException { String originalFilename1 = getOriginalFilename(uploadFile1); // 원본 파일이 있는지 부터 검사. if(StringUtils.isNotBlank(originalFilename1)) { String mime1 = uploadFile1.getContentType(); if(!mime1.startsWith("image/")) { throw new BadRequestException(); } File saveFile1 = new File(saveFolder, originalFilename1); byte[] buffer = new byte[1024]; int cnt = -1; try( InputStream is1 = uploadFile1.getInputStream(); FileOutputStream fos1 = new FileOutputStream(saveFile1); ){ while((cnt = is1.read(buffer))!=-1) { fos1.write(buffer, 0, cnt); } } // try end } // if end return originalFilename1; } }
위의 작업들을 실제 적용하기 위해서는 중복되는게 코드가 많다
중복되는 코드들을 프론트 컨트롤러에 적용해도 되지만 이미 프론트가 완성된 상황에서는 front controller를 수정하기 부담스럽다.
이때 Decorating Filter Pattern으로 front controller 를 수정하지 않고 사용할 수 있다.
D.F.P
Uploaded by Notion2Tistory v1.1.0