본문 바로가기

개발 공부/Spring

코드로 배우는 스프링 웹 프로젝트-part6.파일 업로드 처리-21.파일 업로드 방식

코드로 배우는 스프링 웹 프로젝트-21. 파일 업로드 방식

코드로 배우는 스프링 웹 프로젝트 - 개정판

2019년 7월 10일 인쇄판

 

Part6. 파일 업로드 처리

 

Chapter21. 파일 업로드 방식

 

첨부 파일 서버 전송 방식)

- <form> 태그를 이용하는 방식 : 브라우저의 제한이 없어야 하는 경우 사용.

  일반적으로 페이지 이동과 동시에 첨부파일 업로드. iframe을 이용해 화면의 이동 없이 첨부파일 처리

- Ajax 이용 방식 : 첨부파일을 별도로 처리

  input type=file을 이용하고 Ajax로 처리하는 방식, HTML5의 Drag and Drop 기능이나 jQuery 라이브러리를 이용

 

서버에서 첨부파일을 처리하는 방식)

- cos.jar : 2002년 이후에 개발이 종료되어서 더 이상 사용을 권장하지 않는다

- commons-fileupload : 가장 일반적으로 활용되고 서블릿 스펙 3.0 이전에도 사용 가능.

- 서블릿 3.0 이상 : 3.0 이상부턴 자체적인 파일 업로드 처리가 API상에서 지원됨

 

 

C 드라이브 밑에 upload > temp 폴더를 생성한다.

 

 

21.1 스프링의 첨부파일을 위한 설정

 

 

ex05 프로젝트를 생성한다.

 

java 버전과 aspectj-version, slf4j-version, spring-version 업그레이드.

서블릿 버전 업데이트, lombok 추가.

 

<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
		<org.aspectj-version>1.9.0</org.aspectj-version>
		<org.slf4j-version>1.7.25</org.slf4j-version>
	</properties>
		<dependency><!-- 추가 -->
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.0</version>
			<scope>provided</scope>
		</dependency>
        
        		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

 

2.1.1 web.xml을 이용하는 경우 첨부파일 설정

 

상단을 이렇게 수정

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	id="WebApp_ID" version="3.1">
	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		
		<multipart-config>
			<location>C:\\upload\\temp</location>
			<max-file-size>20971520</max-file-size> <!-- 1MB * 20 -->
			<max-request-size>4194300</max-request-size> <!--  40MB -->
			<file-size-threshold>20971520</file-size-threshold> <!-- 20MB -->
		</multipart-config>
	</servlet>

특정 사이즈의 메모리 사용(file-size-threshold), 업로드되는 파일 저장 공간과 업로드되는 파일 최대 크기, 한번에 올릴 수 있는 최대 크기를 지정할 수 있다.

web.xml 설정은 WAS 자체 설정일 뿐이고 스프링에서 업로드 처리는 MultipartResolver 타입 객체를 빈으로 등록해야 가능.

Web과 관련된 설정이므로 servlet-context.xml 이용

 

	</beans:bean>
	
	<context:component-scan base-package="org.zerock.controller" />
	<beans:bean id="multipartResolver" 
	class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
	</beans:bean>
	
	
</beans:beans>

 

 

21.1.2 Java 설정을 이용하는 경우

 

web.xml이 없어도 문제가 없도록 maven-war-plugin 추가

 

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <configuration>
      <failOnMissingWebXml>false</failOnMissingXml>
    </configuration>
</plugin>

 

org.zerock.config 아래 RootConfig, ServletConfig, WebConfig 클래스 추가.

 

WebConfig 수정

 

MultipartConfig 설정 추가. (서블릿 3.0 이상의 servlet.jar-API 문서는

https://docs.oracle.com/javaee/7/api/javax/servlet/MultipartConfigElement.html 

 

MultipartConfigElement (Java(TM) EE 7 Specification APIs)

MultipartConfigElement(String location, long maxFileSize, long maxRequestSize, int fileSizeThreshold) Constructs an instance with all values specified.

docs.oracle.com

참고 가능.

 

 

@Override
protected void customizedRegistration(ServletRegistration.Dynamic registration) {
	registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
    MultipartConfigElement multipartConfig = new MultipartConfigElement("C:\\upload\\temp", 20971520, 41943040, 20971520);
    registration.setMultipartConfig(multipartConfig);
}

 

ServletConfig수정

 

@Bean
public MultipartResolver multipartResolver() {
	StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
    return resolver;
}

 

21.2 <form> 방식의 파일 업로드.

 

서버상 첨부파일 처리는 컨트롤러에서 이루어진다.

UploadController 작성

GET 방식으로 첨부파일을 업로드할 수 있도록 화면을 처리하는 메서드와 POST 방식 첨부파일 업로드 처리 메서드 추가.

 

package org.zerock.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.log4j.Log4j;

@Controller
@Log4j
public class UploadController {
	@GetMapping("/uploadForm")
	public void uploadForm() {
		log.info("upload form");
	}
}

 

WEB-INF/views 폴더에 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>

 

form 태그만 생성해 file 추가. 실제 전송은 uploadFormAction 경로를 이용해 처리한다.

파일 업로드에서 가장 신경써야 할 부분은 enctype 속성값을 multipart/form-data로 지정하는 것. input type=file의 경우 최근 브라우저에서 multiple 속성을 지원하는데 하나의 input 태그로 여러 파일 업로드 가능.

 

톰캣 경로가 말을 안 들었다. 다음 티스토리로 해결함.

https://jokerkwu.tistory.com/109

 

[ 에러 처리 ] Eclipse에서 Tomcat 실행시 발생 [ Maven 사용시 ][org.springframework.web.context.ContextLoaderListene

실행 잘되던 프로젝트에 maven - update project... 를 클릭후 정상적으로 실행이 안되서 찾아보니 프로젝트 우클릭 - Maven - Update Project 실행시 maven 라이브러리 경로가 삭제되는 현상이 발생한다고 한��

jokerkwu.tistory.com

 

 

21.2.1 MultipartFile 타입

 

스프링 MVC에는 MultipartFile 타입을 제공해서 업로드하는 파일 데이터를 쉽게 처리할 수 있다.

package org.zerock.controller;

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.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import lombok.extern.log4j.Log4j;

@Controller
@Log4j
public class UploadController {
	@GetMapping("/uploadForm")
	public void uploadForm() {
		log.info("upload form");
	}
	
	@PostMapping("/uploadFormAction")
	public void uploadFormPost(MultipartFile[] uploadFile, Model model) {
		for(MultipartFile multipartFile : uploadFile) {
			log.info("--------------------");
			log.info("Upload File Name : " + multipartFile.getOriginalFilename());
			log.info("Upload File size : " + multipartFile.getSize());
		}
	}
}

 

파일 처리는 스프링에서 제공하는 MultipartFile 타입 이용.

 

(아직 한글 처리는 되지 않는다)

 

(IE의 경우 getOriginalFilename이 조금 다르게 처리된다)

 

MultipartFile은 다음의 메서드들을 가지고 있다.

- String getName : 파라미터의 이름 input 태그의 이름

- String getOriginalFileName : 업로드되는 파일 이름

- boolean isEmpty : 파일이 존재하지 않는 경우 true

- long getSize  : 파일 크기

- byte[] getBytes : byte[]로 파일 데이터 반환

- InputStream getInputStream : 파일 데이터와 연결된 InputStream 반환

- transferTo(File file) : 파일 저장

 

파일 저장

 

package org.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.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import lombok.extern.log4j.Log4j;

@Controller
@Log4j
public class UploadController {
	@GetMapping("/uploadForm")
	public void uploadForm() {
		log.info("upload form");
	}
	
	@PostMapping("/uploadFormAction")
	public void uploadFormPost(MultipartFile[] uploadFile, Model model) {
		
		String uploadFolder = "C:\\upload";
		
		for(MultipartFile multipartFile : uploadFile) {
			log.info("--------------------");
			log.info("Upload File Name : " + multipartFile.getOriginalFilename());
			log.info("Upload File size : " + multipartFile.getSize());
			
			File saveFile = new File(uploadFolder, multipartFile.getOriginalFilename());
			
			try {
				multipartFile.transferTo(saveFile);
				
 			} catch(Exception e) {
 				log.error(e.getMessage());;
 			}
		}
	}
}

폴더에도 정상적으로 저장됨.

 

 

21.3 Ajax를 이용하는 파일 업로드

 

Ajax 이용하는 처리는 FormData 객체 이용.

 

	}
	
	@GetMapping("/uploadAjax") 
	public void uploadAjax() {
		log.info("upload ajax");
	}

uploadAjax.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>
<h1>Upload with Ajax</h1>
<div class='uploadDiv'>
	<input type='file' name='uploadFile' multiple>
</div>
<button id='uploadBtn'> Upload </button>
</form>
</body>
</html>
<%@ 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>
<h1>Upload with Ajax</h1>
<div class='uploadDiv'>
	<input type='file' name='uploadFile' multiple>
</div>
<button id='uploadBtn'> Upload </button>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

<script>
$(document).ready(function() {
	$("#uploadBtn").on("click", function(e){
		var formData = new FormData();
		var inputFile = $("input[name='uploadFile']");
		var files = inputFile[0].files;
		console.log(files);
	});
});
</script>
</form>
</body>
</html>

 

jQuery를 이용하는 경우 파일 업로드는 FormData 라는 객체를 이용하게 된다.

쉽게 말해 가상의 form 태그라고 생각하면 된다.

jQuery는 아래처럼 스크립트 복사해 적용

 

 

 

21.3.1 jQuery를 이용한 첨부파일 전송

Ajax를 이용해 첨부파일을 전송하는 경우 가장 중요한 객체는 FormData 타입 객체에 각 파일 데이터를 추가하는 것과 이를 Ajax로 전송할 때 약간의 옵션이 붙어야 한다는 것.

 

<%@ 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>
<h1>Upload with Ajax</h1>
<div class='uploadDiv'>
	<input type='file' name='uploadFile' multiple>
</div>
<button id='uploadBtn'> Upload </button>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

<script>
$(document).ready(function() {
	$("#uploadBtn").on("click", function(e){
		var formData = new FormData();
		var inputFile = $("input[name='uploadFile']");
		var files = inputFile[0].files;
		console.log(files);
		
		//add filedate to formdata
		for(var i=0; i<files.length; i++) {
			formData.append("uploadFile", files[i]);
		}
		
		$.ajax({
			url: '/uploadAjaxAction',
			processData : false,
			contentType: false,
			data: formData,
			type: 'POST',
			success: function(result) {
				alert("Uploaded");
			}
		});
	});
});
</script>
</form>
</body>
</html>

 

첨부 파일 데이터는 formData에 추가한 뒤 Ajax를 통해 전송한다.

이 때 processData와 contentType은 반드시 false로 지정해야 전송된다.

 

 

 

21.3.2 파일 업로드에서 고려해야 하는 점들

- 동일한 이름으로 파일이 업로드 되었을 때 기존 파일이 사라지는 문제

- 이미지 파일의 경우 원본 파일 용량이 크면 썸네일 이미지를 생성해야 하는 문제

- 이미지 파일과 일반 파일을 구분해서 다운로드 혹은 페이지에서 조회하도록 처리하는 문제

- 첨부파일 공격에 대비하기 위한 업로드 파일의 확장자 제한