-
[6-ch21~22 Spring ] 파일 업로드 방식 ( 중복처리, 섬네일 ,, )Back-End/Spring Legacy 2023. 5. 10. 16:50
일단 파일 업로드 방식을 알아보기전 c드라이브 밑에 uploadBook 폴더와 임시 업로드 파일을 저장할 temp 폴더를 생성해둔다.
첨부파일을 서버에 전송하는 방식은 크게 <form> 태그를 이용해 업로드하는 방식과 Ajax를 이용하는 방식으로 나눠볼 수 있다.
📌 <form> 태그를 이용하는 방식 : 브라우저의 제한이 없어야 하는 경우 사용
- 일반적으로 페이지 이동과 동시에 첨부파일을 업로드하는 방식
- <iframe>을 이용해 화면의 이동 없이 첨부파일을 처리하는 방식
📌 Ajax를 이용하는 방식 : 첨부파일을 별도로 처리하는 방식
- <input type='file'>을 이용하고 Ajax로 처리하는 방식
- HTML5의 Drag And Drop 기능이나 jQuery 라이브러리를 이용해서 처리하는 방식코드로보는 스프링 웹 프로젝트 책에서는 Ajax를 위주로 처리하였다.
📑 web.xml 설정
📑 1. <form>방식의 파일 업로드
<uploadForm.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <!-- 파일 업로드에서 가장 신경써야 하는 부분이다. --> <form action="uploadFormAction" method="post" enctype="multipart/form-data"> <input type='file' name='uploadFile' multiple> <button>Submit</button> </form> </body> </html>
<UploadController.java>
package com.zerock.controller; import java.io.File; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.multipart.MultipartFile; import lombok.extern.log4j.Log4j2; @Controller @Log4j2 public class UploadController { @GetMapping("/uploadForm") public void uploadForm() { log.info("upload form"); } @PostMapping("/uploadFormAction") public void uploadFormPost(MultipartFile[] uploadFile, Model model) { String uploadFolder = "C:\\uploadBook"; for(MultipartFile multipartFile : uploadFile) { log.info("=========================="); log.info("upload file name: "+ multipartFile.getOriginalFilename()); log.info("upload file size: " + multipartFile.getSize()); //java.io.File.File(String parent, String child) // 원래 파일의 이름으로 c드라이브 upload 폴더에 저장된다. File saveFile = new File(uploadFolder, multipartFile.getOriginalFilename()); try { multipartFile.transferTo(saveFile); }catch(Exception e) { log.error(e.getMessage()); } } } }
📑 2. Ajax를 이용한 파일 업로드
<uploadAjax.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%-- 이 DTD는 모든 HTML 요소와 속성들뿐만 아니라 더 이상 사용되지 않거나 아직 정식으로 포함되지 못한 요소들까지도 포함하고 있습니다. 하지만 프레임셋(frameset) 콘텐츠의 사용은 허용하지 않습니다. --%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>upload whit ajax</title> </head> <script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script> <script type="text/javascript"> $(document).ready(function(){ $("#uploadBtn").on("click", function(e){ //FormData는 가상의 <form> 태그와 같다. //ajax를 이용하는 파일 업로드는 FormData를 이용해 필요한 파라미터를 담아 전송한다. var formData = new FormData(); var inputFile = $("input[name='uploadFile']"); //<input>태그 그 자체를 가져온다. console.log(inputFile[0]); //.files로 files에 배열 객체를 참조할 수 있다. var files = inputFile[0].files; console.log(files); //add file data to format for(var i=0; i<files.length; i++){ formData.append("uploadFile", files[i]); } //ajax를 통해 서버의 url로 요청을 보낸다. 그때 formData 객체 자체를 전달함 // processData와 contentType은 반드시 false로 지정해야 됨. $.ajax({ url : '/uploadAjaxAction', processData : false, contentType : false, data : formData, type : 'POST', success : function(result){ alert("uploaded"); } }); //$.ajax }); }); </script> <body> <h1>Upload with Ajax</h1> <div class='uploadDiv'> <input type='file' name='uploadFile' multiple> </div> <button id='uploadBtn'>Upload</button> </body> </html>
<uploadController.java>
package com.zerock.controller; import java.io.File; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.multipart.MultipartFile; import lombok.extern.log4j.Log4j2; @Controller @Log4j2 public class UploadController { @GetMapping("/uploadAjax") public void uploadAjax() { log.info("upload ajax"); } @PostMapping("/uploadAjaxAction") public void uploadAjaxPost(MultipartFile[] uploadFile) { log.info("update ajax post......"); String uploadFolder = "C:\\uploadBook"; for(MultipartFile multipartFile : uploadFile) { log.info("=========================="); log.info("upload file name: "+ multipartFile.getOriginalFilename()); log.info("upload file size: " + multipartFile.getSize()); String uploadFileName = multipartFile.getOriginalFilename(); //IE has file path uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") +1); log.info("only file name: " + uploadFileName ); File savaFile = new File(uploadFolder, uploadFileName); try { multipartFile.transferTo(savaFile); }catch(Exception e) { log.error(e.getMessage()); } } } }
Ajax를 이용해 첨부파일을 전송하는 경우 FormData라는 객체를 이용하게 된다. FormData는 쉽게 말해 가상의 <form> 태그와 같다고 생각하면 된다. Ajax를 이용하는 파일 업로드는 FormData를 이용해 필요한 파라미터를 담아 전송하는 방식이다.
📑 파일 업로드 상세 처리
📌 1. 파일의 확장자나 크기의 사전 처리
js로 특정 크기 이상의 파일과, 특정 확장자를 가진 파일은 업로드할 수 없도록 처리한다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%-- 이 DTD는 모든 HTML 요소와 속성들뿐만 아니라 더 이상 사용되지 않거나 아직 정식으로 포함되지 못한 요소들까지도 포함하고 있습니다. 하지만 프레임셋(frameset) 콘텐츠의 사용은 허용하지 않습니다. --%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>upload whit ajax</title> </head> <script src="https://code.jquery.com/jquery-3.6.4.min.js" integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8=" crossorigin="anonymous"></script> <script type="text/javascript"> $(document).ready(function(){ //파일 크기와 확장자 처리 //regex = Regular Expression 정규표현식 var regex = new RegExp("(.*?)\.(exe|sh|zip|alz$)"); var maxSize = 5242880; // 5MB function checkExtension(fileName, fileSize){ if(fileSize > maxSize){ alert("파일 사이즈 초과"); return false; } if(regex.test(fileName)){ alert("해당 종류의 파일은 업로드할 수 없습니다."); return false; } return true; } $("#uploadBtn").on("click", function(e){ //FormData는 가상의 <form> 태그와 같다. //ajax를 이용하는 파일 업로드는 FormData를 이용해 필요한 파라미터를 담아 전송한다. var formData = new FormData(); var inputFile = $("input[name='uploadFile']"); //<input>태그 그 자체를 가져온다. console.log(inputFile[0]); //.files로 files에 배열 객체를 참조할 수 있다. var files = inputFile[0].files; console.log(files); //add file data to format for(var i=0; i<files.length; i++){ if(!checkExtension(files[i].name, files[i].size)){ return false; } formData.append("uploadFile", files[i]); } //ajax를 통해 서버의 url로 요청을 보낸다. 그때 formData 객체 자체를 전달함 // processData와 contentType은 반드시 false로 지정해야 됨. $.ajax({ url : '/uploadAjaxAction', processData : false, contentType : false, data : formData, type : 'POST', success : function(result){ alert("uploaded"); } }); //$.ajax }); }); </script> <body> <h1>Upload with Ajax</h1> <div class='uploadDiv'> <input type='file' name='uploadFile' multiple> </div> <button id='uploadBtn'>Upload</button> </body> </html>
📌 2. 중복된 이름의 첨부파일 처리
첨부파일을 보관하는 폴더를 생성하는 작업은 한 번에 폴더를 생성하거나 존재하는 파일을 이용하는 방식을 사용한다. java.io.File에 존재하는 mkdirs()를 이용하면 필요한 상위 폴더까지 한 번에 생성할 수 있다.
또한 중복된 파일의 이름을 다르게하기 위해서 java.util.UUID의 값을 이용해 처리하였다.
@PostMapping("/uploadAjaxAction") public void uploadAjaxPost(MultipartFile[] uploadFile) { log.info("update ajax post......"); String uploadFolder = "C:\\uploadBook"; //make folder ---------- //java.io.File.File(String parent, String child) // 오늘 날짜 이름으로 파일 생성해서 c드라이브 upload 폴더에 저장된다. File uploadPath = new File(uploadFolder, getFolder()); log.info("upload path: " + uploadPath); if(uploadPath.exists() == false) { uploadPath.mkdirs(); // 새로운 폴더 생성 } for(MultipartFile multipartFile : uploadFile) { log.info("=========================="); log.info("upload file name: "+ multipartFile.getOriginalFilename()); log.info("upload file size: " + multipartFile.getSize()); String uploadFileName = multipartFile.getOriginalFilename(); //IE has file path uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") +1); log.info("only file name: " + uploadFileName ); //중복 방지 UUID 적용 UUID uuid = UUID.randomUUID(); uploadFileName = uuid.toString()+"_"+uploadFileName; //File savaFile = new File(uploadFolder, uploadFileName); File saveFile = new File(uploadPath, uploadFileName); try { multipartFile.transferTo(saveFile); }catch(Exception e) { log.error(e.getMessage()); } } } private String getFolder() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = new Date(); String str = sdf.format(date); //File.separator 는 프로그램이 실행 중인 OS에 해당하는 구분자를 리턴 return str.replace("-", File.separator); }
📌 3. 섬네일 처리하기
📑 업로드된 파일의 데이터 반환
이미지 파일의 경우 섬네일 생성시 다음과 같이 폴더 이름이 "s_"로 시작하게 처리했다. 이미지 파일이 아닌 경우 섬네일 생성 없이 그냥 파일만 업로드된다.
@PostMapping("/uploadAjaxAction") public void uploadAjaxPost(MultipartFile[] uploadFile) { log.info("update ajax post......"); String uploadFolder = "C:\\uploadBook"; //make folder ---------- //java.io.File.File(String parent, String child) // 오늘 날짜 이름으로 파일 생성해서 c드라이브 upload 폴더에 저장된다. File uploadPath = new File(uploadFolder, getFolder()); log.info("upload path: " + uploadPath); if(uploadPath.exists() == false) { uploadPath.mkdirs(); // 새로운 폴더 생성 } for(MultipartFile multipartFile : uploadFile) { log.info("=========================="); log.info("upload file name: "+ multipartFile.getOriginalFilename()); log.info("upload file size: " + multipartFile.getSize()); String uploadFileName = multipartFile.getOriginalFilename(); //IE has file path uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") +1); log.info("only file name: " + uploadFileName ); //중복 방지 UUID 적용 UUID uuid = UUID.randomUUID(); uploadFileName = uuid.toString()+"_"+uploadFileName; //File savaFile = new File(uploadFolder, uploadFileName); File saveFile = new File(uploadPath, uploadFileName); try { multipartFile.transferTo(saveFile); //check image type file if(checkImageType(saveFile)) { FileOutputStream thumbnail = new FileOutputStream(new File(uploadPath, "s_" + uploadFileName)); Thumbnailator.createThumbnail(multipartFile.getInputStream(), thumbnail,100,100); thumbnail.close(); } }catch(Exception e) { log.error(e.getMessage()); } } } //폴더 생성 위한 현재 날짜 추출 private String getFolder() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = new Date(); String str = sdf.format(date); //File.separator 는 프로그램이 실행 중인 OS에 해당하는 구분자를 리턴 return str.replace("-", File.separator); } // 섬네일 이미지 생성 위한 이미지 파일 판단 private boolean checkImageType(File file) { try { String contentType = Files.probeContentType(file.toPath()); //startsWith() 메서드는 어떤 문자열이 특정 문자로 시작하는지 확인하여 결과를 true 혹은 false로 반환합니다. return contentType.startsWith("image"); }catch(IOException e){ e.printStackTrace(); } return false; }
📑 pom.xml
확인하려면 아래 클릭
더보기<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zerock</groupId> <artifactId>controller</artifactId> <name>ex05</name> <packaging>war</packaging> <version>1.0.0-BUILD-SNAPSHOT</version> <properties> <java-version>1.6</java-version> <org.springframework-version>5.3.21</org.springframework-version> <org.aspectj-version>1.6.10</org.aspectj-version> <org.slf4j-version>1.6.6</org.slf4j-version> </properties> <dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework-version}</version> <exclusions> <!-- Exclude Commons Logging in favor of SLF4j --> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework-version}</version> </dependency> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${org.aspectj-version}</version> </dependency> <!--Log4j2--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.18.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.18.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.18.0</version> </dependency> <!-- @Inject --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--Lombook--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> <!-- json 추가 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> </dependency> <!--섬네일 이미지 생성 --> <!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator --> <dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.8</version> </dependency> <!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.7</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-eclipse-plugin</artifactId> <version>2.9</version> <configuration> <additionalProjectnatures> <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature> </additionalProjectnatures> <additionalBuildcommands> <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand> </additionalBuildcommands> <downloadSources>true</downloadSources> <downloadJavadocs>true</downloadJavadocs> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.6</source> <target>1.6</target> <compilerArgument>-Xlint:all</compilerArgument> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.2.1</version> <configuration> <mainClass>org.test.int1.Main</mainClass> </configuration> </plugin> </plugins> </build> </project>
반응형'Back-End > Spring Legacy' 카테고리의 다른 글
[Spring] MyBatis의 resultMap 살펴보기 (0) 2023.05.24 [MyBatis] 동적 쿼리 trim 문법 알아보기 (0) 2023.04.17 [Spring] Log4j2 환경설정 , (+ log.info 에러 ) (0) 2023.04.04 [jsp] <%@ include%>와 jsp:include 차이 (0) 2022.11.30 [Spring MyBatis] selectKey 사용하기 (0) 2022.11.17