Keep going

파일 업로드 방식 본문

Records/Spring Framework

파일 업로드 방식

코딩천재홍 2021. 3. 28. 10:25

 

첨부파일을 서버에 전송하는 방식은 크게 <form> 태그를 이용해서 업로드하는 방식과 Ajax를 이용하는 방식으로 나눠볼 수 있다.

  • <form> 태그를 이용하는 방식 : 브라우저의 제한이 없어야 하는 경우에 사용
    • 일반적으로 페이지 이동과 동시에 첨부파일을 업로드하는 방식
    • <iframe> 을 이용해서 화면의 이동 없이 첨부파일을 처리하는 방식
  • Ajax를 이용하는 방식 : 첨부파일을 별도로 처리하는 방식
    • <input type = 'file>을 이용하고 Ajax로 처리하는 방식
    • HTML5의 Drag and Drop 기능이나 JQuery 라이브러리를 이용해서 처리하는 방식

브라우저 상에서 첨부파일을 처리하는 방식은 다양하게 있지만, 서버 쪽에서의 처리는 거의 대부분 비슷하다.

응답을 HTML 코드로 하는지 아니면 JSON 등으로 처리하는지 정도의 구분만 하면 된다.

 

 

 

서버에서 주의해야 하는 점 - 첨부파일의 처리를 위해서 어떤 종류의 라이브러리나 API 등을 활용 할 것인지

  • cos.jar : 2002년도 이후에 개발이 종료되었으므로, 더 이상 사용하는 것을 권장 하지 않는다.
  • commons-fileupload : 가장 일반적으로 많이 활용되고, 서블릿 스펙 3.0 이전에도 사용 가능
  • 서블릿 3.0 이상 - 3.0 이상부터는 자체적인 파일 업로드 처리가 API 상에서 지원 (Tomcat 7버전 이후)

 


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

 

 

  • porm.xml
<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>
 
<!-- Servlet -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
 
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.0</version>
    <scope>provided</scope>
</dependency>     
cs

 

 

 

  • web.xml
    • 프로젝트가 web.xml을 이용하는 경우라면 첨부파일의 처리에 대한 설정 역시 web.xml을 이용해서 처리
    • 프로젝트 생성 될 때 서블릿 버전 2.5버전이므로 이를 수정하는 작업 필요
<?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">
 
cs

 

<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>
cs

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

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

 

 

 

  • servlet-context.xml
<beans:bean id="multipartResolver" 
        class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</beans:bean>
cs

 


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

서버상에서 첨부파일의 처리는 컨트롤러에서 이루어지므로, UploadController를 작성한다.

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

 

 

  • UploadController
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");
    }
}
cs

UploadController에는 클래스 선언부에 @RequestMapping이 적용되지 않았으므로, WEB-INF/views 폴더에 uploadForm.jsp 파일을 추가한다.

 

 

 

  • 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>
cs

- uploadForm.jsp는 간단하게 <form> 태그만을 생성하고 <input type ='file'> 을 추가한다.

- 실제 전송은 uploadFormAction 경로를 이용해서 처리한다.

- 파일 업로드에서 가장 신경 써야 하는 부분은 enctype의 속성값을 multipart/form-data로 지정하는 것이다.

- <input type='file'>의 경우 최근 브라우저에서는 multiple이라는 속성을 지원하는데 이를 이용하면 하나의 <input> 태그로 한꺼번에 여러 개의 파일을 업로드 할 수 있다.

 

 

 

  • MultipartFile 타입

    • 스프링 MVC 에서는 MultipartFile 타입을 제공해서 업로드 되는 파일 데이터를 쉽게 처리 할 수 있다.
    • <input type ='file' name='uploadFile'>의 name 속성으로 변수를 지정해서 처리한다.

 

  • UploadController 
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.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());
        }
    }
}
cs

- 파일 처리는 스프링에서 제공하는 MultipartFile 이라는 타입을 이용한다.

- 화면에서 첨부파일을 여러 개 선택할 수 있으므로 배열 타입으로 설정한 후 파일을 업로드 할 수 있다.

 

 

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

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

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

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

- long getSize  : 파일 크기

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

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

- transferTo(File file) : 파일 저장

 

 

 

  • UploadController - 파일 저장
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.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());
             }
        }
    }
}
cs

transforTo()의 파라미터로는 java.io.File의 객체를 지정하면 되기 때문에 업로드 되는 원래 파일의 이름으로 C 드라이브 upload 폴더에 원래 이름으로 저장한다.

 

 


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

첨부파일을 업로드하는 또 다른 방식은 Ajax를 이용해서 파일 데이터만을 전송하는 방식이다.

Ajax를 이용하는 첨부파일 처리는 FormData라는 객체를 이용한다.

 

 

  • UploadController - GET 방식으로 첨부파일을 업로드하는 페이지 위함
@GetMapping("/uploadAjax"
    public void uploadAjax() {
        log.info("upload ajax");
    }
cs

 

 

  • 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>
 
<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>
 
</body>
</html>
cs

uploadAjax.jsp는 순수한 JavaScript를 이용해서 처리할 수도 있지만, jQuery를 이용해서 처리하는 것이 편리하다.

uploadAjax.jsp 내에 jQuery 라이브러리의 경로를 추가하고 <script>를 이용해서 첨부파일을 처리한다.

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

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

 

 

 

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

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

 

    • uploadAjax.jsp
<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>
cs

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

이때 processData와 contentType은 반드시 'false'로 지정해야만 전송되므로 주의해야 한다.

 

 

 

  • UploadController - uploadAjaxPost
@PostMapping("/uploadAjaxAction")
    public void uploadAjaxPost(MultipartFile[] uploadFile) {
        
        log.info("update ajax post.....");
        
        String uploadFolder = "C:\\upload";
        
        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 saveFile = new File(uploadFolder, uploadFileName);
            
            try {
                multipartFile.transferTo(saveFile);
            }catch (Exception e) {
                log.error(e.getMessage());
            }
            
        }
    }
cs

- 기존의 form 태그를 이용하던 방식과 차이가 없지만 조금 뒤에 Ajax 방식으로 결과 데이터를 전달하면서 리턴 타입이 달라지도록 한다.

- 파라미터에서는 Ajax 방식을 이용하기 때문에 Model을 사용할 일이 없으므로 사용하지 않는다.

- IE의 경우에는 전체 파일 경로가 전송되므로, 마지막 \ 기준으로 잘라낸 문자열이 실제 파일 이름이 된다

 

 

 

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

    • 동일한 이름으로 파일이 업로드 되었을 때 기존의 파일이 사라지는 문제
    • 이미지 파일의 경우에는 원본 파일의 용량이 큰 경우 섬네일 이미지를 생성해야 하는 문제
    • 이미지 파일과 일반 파일을 구분해서 다운로드 혹은 페이지에서 조회하도록 처리하는 문제
    • 첨부파일 공격에 대비하기 위한 업로드 파일의 확장자 제한

 


출처 : 코드로 배우는 스프링 웹 프로젝트 [구멍가게 코딩단]

'Records > Spring Framework' 카테고리의 다른 글

브라우저에서 섬네일 처리  (0) 2021.03.28
파일 업로드 상세 처리  (0) 2021.03.28
댓글과 댓글 수에 대한 처리  (0) 2021.03.26
스프링에서 트랜잭션 관리  (0) 2021.03.22
AOP라는 패러다임  (0) 2021.03.22
Comments