ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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>
    반응형

    댓글

Designed by Tistory.