안녕하세요. 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;
onFileUpload 부분을 보면 전달받을 Food데이터를 JSON.stringify를 통해 문자열로 변경하여 FormData에 담아주고 있습니다.
만약 FormData에 문자열이 아닌 JSON자체를 담게 되면 스프링에서 받을 수 없기 때문에 파일 데이터와 함께 추가적인 데이터를 보내고 싶을 땐 문자열로 변경하여 FormData에 담고 스프링에서 전달받은 문자열을 클래스로 다시 변경해주는 작업을 진행해야 합니다.
해당 코드를 실행하게되면 화면은 다음과 같이 나옵니다.
여기서 파일 선택버튼을 클릭하여 미리 만들어 둔 윈도우와 알집 사진을 선택해보겠습니다.
파일 선택을 완료한 후 파일 업로드 버튼을 누르게 되면 스프링에 데이터가 전달되며 구현된 코드가 실행이 됩니다.
콘솔 창에 출력은 다음과 같이 나옵니다.
fileId= 16148666723217699
originName= 알집사진
fileExtension= PNG
fileSize= 3798
fileId= 16148666723221824
originName= 윈도우모양
fileExtension= PNG
fileSize= 110725
food= Food [name=피자, price=13500]
그리고 업로드 위치로 지정했던 곳에는 선택된 파일들이 임의의 이름으로 업로드가 된 것을 확인할 수 있습니다. (스프링에서는 \를 하나 더 붙여줘야 합니다)
이상으로 MultipartFile을 이용한 파일 업로드 방법에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'Spring > Spring' 카테고리의 다른 글
[Spring] MyBatis ↔ Repository를 연결하는 다양한 방법들 (1) | 2021.03.16 |
---|---|
[Spring] 파일 업로드 - MultipartRequest(With. React) (0) | 2021.03.05 |
[Spring] Swagger (0) | 2021.03.03 |
[Spring] Lombok (0) | 2021.03.02 |
[Spring] Filter / Interceptor (0) | 2021.03.01 |
댓글