Multipart

클라이언트가 파일을 업로드할 때 파일을 http 프로토콜의 body부분을 통해서 전송한다. 

이 때 파일을 여러 개 전송하게 되면 body부분에 파일이 여러 개 나눠서 전송되는데 이를 multipart라고 한다.

 

 

이렇게 여러 개의 나뉘어서 들어오는 multipart data를 쉽게 처리하기 위해서 라이브러리를 이용하면 된다.

대표적으로 아파치 재단의 commons-fileupload 라이브러리가 있다. 

 

 

필요한 준비물

 --> commons-fileupload 라이브러리 , commons-io 라이브러리, MultipartResolver bean추가

 

아래 코드를 이용해서 라이브러리를 추가하자.

<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.2.1</version>
</dependency>

<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>1.4</version>
</dependency>

 

다음으로 DisDispatcherServlet에 해당하는 클래스에 multipartResolver bean을 생성한다.

setMaxUploadSize : 업로드할 파일의 최대 용량을 설정 

10485760바이트 : 1024 * 1024 * 10 -> 10 MB

(1024 byte : 1 KB, 1024 KB = 1MB)

	@Bean
	public MultipartResolver multipartResolver() {
		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
		multipartResolver.setMaxUploadSize(10485760);
		return multipartResolver;
	}

 

설정이 완료된 후 파일을 업로드 할 페이지를 만들었다.

form태그에서 enctype은 "multipart/form-data"로 해줘야 한다! (인코딩 방식)

enctype을 multipart/form-data로 해주기 위해서 method는 post방식으로 해줘야 한다.  

(참고 : tcpschool.com/html-tag-attrs/form-enctype

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Upload Form</title>
</head>
<body>
	<h1>Upload Form</h1>
	<br>
	<br>
	<form action="upload"  method="post" enctype="multipart/form-data">
		file : <input type="file" name="file"><br>
		<input type="submit"  value="제출">
	</form>
</body>
</html>

 

 

파일을 업로드 후 제출을 클릭했을 때 작동할 컨트롤러를 작성했다.

코드를 쓰면서 getName과 getOriginalFilename의 차이점을 몰랐다.

직접 출력해보니 아래 사진과 같이 getName은 파라미터로 넘어온 키에 해당하였고

getOriginalFilename은 실제 업로드한 파일의 이름 값을 가져왔다.

 

FileOutputStream과 InputStream은 바이트 단위로 데이터를 읽어 들인다. 이 점 기억하자!

FileOutputStream은 해당 경로에 파일을 생성한다. (일단 무조건 생성한다.)

InputStream은 파일을 읽어들인다.

...
@PostMapping("/upload")
	public String upload(@RequestParam("file") MultipartFile file) {
		System.out.println("파일 이름 : " + file.getName());
		System.out.println("파일 이름 : " + file.getOriginalFilename());
		System.out.println("파일 크기 : " + file.getSize());
		try (
			FileOutputStream outputStream = new FileOutputStream("c:/tmp/" + file.getOriginalFilename());
			InputStream inputStream = file.getInputStream();
		) {
			int readCount = 0;
            
            // 1024바이트씩 읽어 들이기 위한 설정
			byte[] buffer = new byte[1024];  
			
            // inputStream.read(buffer) : 1024바이트 씩 데이터를 읽다가 
            // 읽을 수 있는 데이터가 없는 경우 -1을 반환한다.
            while((readCount = inputStream.read(buffer)) != -1) {
             // 읽어들인 buffer를 readCount수 만큼 저장한다.
				outputStream.write(buffer, 0, readCount);
			}
		}
		catch (Exception e) {
			throw new RuntimeException("file Save Error");
		}
		return "uploadok";
	}

 

 

위 코드를 이해한대로 정리했다.

 

FileOutputStream outputStream = new FileOutputStream("경로" + "파일명");

InputStream inputStream = file.getInputStream();  입력된 file로 부터 읽어들임

이 코드들을 try()안에 작성한 이유는 이렇게 하면 나중에 따로 close를 할 필요가 없기 때문이다. (자동으로 닫아줌)

 

int readCount : 읽어 들일 데이터의 수를 저장하는 변수
byte[] buffer = new byte[1024]  : 바이트 형식의 배열로 1024만큼의 크기를 가진 변수 생성

(데이터를 바이트로 저장하는데 이를 담을 변수)

 

while( (readCount = inputStream.read( buffer ) ) != -1) : inputStream이 buffer의 크기 만큼 읽고  buffer에 저장한다.

그리고 읽은 수 를 반환한다. 만약 더이상 읽을 수 있는 데이터가 없으면 -1을 반환한다.

이 부분은 잘 몰라서 메소드 설명부분을 번역해봤다.

 

 

outputStream.write(buffer, 0, readCount) : 읽어들인 데이터가 저장된 buffer를 0번쨰 부터 readCount 수 만큼 저장(write)한다.

try (
	FileOutputStream outputStream = new FileOutputStream("c:/tmp/" + file.getOriginalFilename());
	InputStream inputStream = file.getInputStream();
	) {
		int readCount = 0;
		byte[] buffer = new byte[1024];
        
        while((readCount = inputStream.read(buffer)) != -1) {
			outputStream.write(buffer, 0, readCount);
		}
	}
	catch (Exception e) {
		throw new RuntimeException("file Save Error");
	}
	return "uploadok";

 

실행 결과 다음과 같이 파일이 잘 만들어졌다. 

 

+ Recent posts