본문 바로가기
Spring/Spring

[Spring] 파일 업로드 - MultipartFile(With. React)

by J4J 2021. 3. 4.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 MultipartFile을 이용한 파일 업로드 방법에 대해 적어보는 시간을 가져보려고 합니다.

 

 

적용 방법

 

※ 스프링과 관련된 코드는 모두 STS-3.9.12.RELEASE 버전을 기준으로 작성되었습니다.

 

[ 1. pom.xml에 dependency 추가 ]

 

<!-- File Upload -->
<dependency> <!-- 파일 업로드를 위한 dependency -->
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.4</version>
</dependency>

<!-- Data Binding -->
<dependency> <!-- JSON 데이터 처리를 위한 dependency -->
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.8</version>
</dependency>

 

 

[ 2. servlet-context에 multipartResolver 추가 ]

 

[ 2-1. xml에 설정할 경우 ]

 

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	...
	
	<!-- 파일 업로드를 위한 multipartResolver 빈 등록 -->
	<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<beans:property name="maxUploadSize" value="2000000000" />
		<beans:property name="defaultEncoding" value="UTF-8" />
	</beans:bean>
	
</beans:beans>

 

 

[ 2-2. Java에 설정 할 경우 ]

 

package com.spring.multipartFile.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.spring.multipartFile.controller"})
public class ServletContext implements WebMvcConfigurer {
	
	...
	
	// 파일 업로드를 위한 multipartResolver 빈 등록
	@Bean
	public MultipartResolver multipartResolver() throws Exception {
		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
		multipartResolver.setMaxUploadSize(2000000000);
		multipartResolver.setDefaultEncoding("UTF-8");
		
		return multipartResolver;
	}
}

 

 

반응형

 

 

[ 3. 화면에서 파일과 함께 전달받을 DTO 클래스 생성 (com.spring.multipartFile.dto.Food) ]

 

package com.spring.multipartFile.dto;

public class Food {
	private String name;
	private int price;
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public int getPrice() {
		return price;
	}
	
	public void setPrice(int price) {
		this.price = price;
	}
	
	@Override
	public String toString() {
		return "Food [name=" + name + ", price=" + price + "]";
	}
}

 

 

[ 4. Controller 클래스 생성 (com.spring.multipartFile.controller.MyController) ]

 

package com.spring.multipartFile.controller;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Random;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.multipartFile.dto.Food;

@RestController
@CrossOrigin("*") // 모든 출처에 대해 오픈 (CORS)
public class MyController {

	@PostMapping("/uploadFiles")
	public ResponseEntity<Object> uploadFiles(MultipartFile[] multipartFiles, String stringFood) {
		String UPLOAD_PATH = "F:\\myUpload"; // 업로드 할 위치
		
		try {
        		// JSON데이터는 파일 데이터와 같이 못 보내기 때문에 문자열로 받아와서 JSON으로 변환
            		// JSON데이터로 같이 보내는 방법을 아시는 분은 댓글로 알려주시면 감사드립니다.
			Food food = new ObjectMapper().readValue(stringFood, Food.class); // String to JSON
			
			for(int i=0; i<multipartFiles.length; i++) {
				MultipartFile file = multipartFiles[i];
                
				String fileId = (new Date().getTime()) + "" + (new Random().ints(1000, 9999).findAny().getAsInt()); // 현재 날짜와 랜덤 정수값으로 새로운 파일명 만들기
				String originName = file.getOriginalFilename(); // ex) 파일.jpg
				String fileExtension = originName.substring(originName.lastIndexOf(".") + 1); // ex) jpg
				originName = originName.substring(0, originName.lastIndexOf(".")); // ex) 파일
				long fileSize = file.getSize(); // 파일 사이즈
				
				File fileSave = new File(UPLOAD_PATH, fileId + "." + fileExtension); // ex) fileId.jpg
				if(!fileSave.exists()) { // 폴더가 없을 경우 폴더 만들기
					fileSave.mkdirs();
				}
                
				file.transferTo(fileSave); // fileSave의 형태로 파일 저장
				
				System.out.println("fileId= " + fileId);
				System.out.println("originName= " + originName);
				System.out.println("fileExtension= " + fileExtension);
				System.out.println("fileSize= " + fileSize);
			}
			
			System.out.println("food= " + food);
		} catch(IOException e) {
			return new ResponseEntity<Object>(null, HttpStatus.CONFLICT);
		}
		
		return new ResponseEntity<Object>("Success", HttpStatus.OK);
	}
}

 

 

React 코드 및 실행 화면

 

클라이언트 구성을 위한 부분은 리액트에 타입 스크립트를 얹어서 구현했습니다.

 

스프링에 관련된 내용이 아니기 때문에 참고하실분들만 참고하시면 될 것 같습니다.

 

import * as React from 'react';
import axios from 'axios';

const myPage = (): JSX.Element => {
    const fileList: File[] = [];

    const onSaveFiles = (e: React.ChangeEvent<HTMLInputElement>) => {
        const files: FileList | null = e.target.files;
        const fileArray = Array.prototype.slice.call(files);

        fileArray.forEach((file) => {
            fileList.push(file);
        });
    };

    const onFileUpload = async () => {
        const formData = new FormData();

        fileList.forEach((file) => {
            formData.append('multipartFiles', file);
        });

        const Food = {
            name: '피자',
            price: 13500,
        };

        formData.append('stringFood', JSON.stringify(Food));

        const res = await axios.post('http://localhost:8080/multipartFile/uploadFiles', formData);

        console.log(res.data);
    };

    return (
        <div>
            <input type="file" multiple onChange={onSaveFiles} />
            <button onClick={onFileUpload}>파일 업로드</button>
        </div>
    );
};

export default myPage;

 

 

728x90

 

 

onFileUpload 부분을 보면 전달받을 Food데이터를 JSON.stringify를 통해 문자열로 변경하여 FormData에 담아주고 있습니다.

 

만약 FormData에 문자열이 아닌 JSON자체를 담게 되면 스프링에서 받을 수 없기 때문에 파일 데이터와 함께 추가적인 데이터를 보내고 싶을 땐 문자열로 변경하여 FormData에 담고 스프링에서 전달받은 문자열을 클래스로 다시 변경해주는 작업을 진행해야 합니다.

 

해당 코드를 실행하게되면 화면은 다음과 같이 나옵니다.

 

React 실행화면

 

 

여기서 파일 선택버튼을 클릭하여 미리 만들어 둔 윈도우와 알집 사진을 선택해보겠습니다.

 

파일 선택

 

 

파일 선택을 완료한 후 파일 업로드 버튼을 누르게 되면 스프링에 데이터가 전달되며 구현된 코드가 실행이 됩니다.

 

콘솔 창에 출력은 다음과 같이 나옵니다.

 

fileId= 16148666723217699
originName= 알집사진
fileExtension= PNG
fileSize= 3798
fileId= 16148666723221824
originName= 윈도우모양
fileExtension= PNG
fileSize= 110725
food= Food [name=피자, price=13500]

 

 

그리고 업로드 위치로 지정했던 곳에는 선택된 파일들이 임의의 이름으로 업로드가 된 것을 확인할 수 있습니다. (스프링에서는 \를 하나 더 붙여줘야 합니다)

 

업로드 확인

 

 

 

 

이상으로 MultipartFile을 이용한 파일 업로드 방법에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

728x90
반응형

댓글