스프링 부트로 파일을 업로드 하는 것은 아주 단순한 설정만으로도 가능합니다.
스프링 부트의 파일 업로드와 관련된 설정은
1) 별도의 파일 업로드 라이브러리(commons -fileload)등을 이용하는 경우,
2) Servlet 3 버전부터 추가된 자체적인 파일 업로드 라이브러리를 이용하는 방식
으로 구분할수 있습니다.
만약 프로젝트를 실행하는 WAS의 버전이 낮은 경우나 WAS가 아닌 환경에서 스프링부트 프로젝트를 실행한다면 별도의 라이브러리를 사용하는 것이 좋지만 서블릿 기반으로 설정해봅니다.
대부분의 웹 애플리케이션은 이미지 파일 등을 업로드할 때 섬네일을 만들어서 처리합니다.
->섬네일을 만들어서 목록이나 조회화면에서 보이도록 하고, 조회 화면에서는 섬네일을 클릭하면 원본 파일을 보이도록 작성합니다.
스프링 부트 프로젝트를 내장된 Tomcat을 이용하여 실행한다면 별도의 추가적인 라이브러리 없이 application.properties 파일을 수정하는 것만으로 충분합니다.
실제 업로드된 파일 처리는 컨트롤러로 처리합니다.
이에 관련해서 스프링에서는 MultipartFile 타입을 제공하므로 추가적인 처리 필요없이 바로 사용이 가능합니다.
예제에서는 파일 업로드와 관련된 모든 작업은 Ajax방식으로 처리할 것이므로 업로드 결과에 대한 별도의 화면을 작성할 필요가 없습니다.
모든 업로드 결과는 JSON 형태로 제공하도록 작성합니다.
컨트롤러는 controller 패키지를 생성하고, UploadController 클래스로 추가합니다.
@RestController
@Log4j2
public class uploadController{
@PostMapping("/uploadAjax")
public void uploadFile(MultipartFile[] uploadFiles){
//MultipartFile은 단건만 배열로 설정하면 다수의 파일을 받을 수있습니다.
//배열을 활용하면 동시에 여러개의 파일 정보를 처리할 수 있으므로 화면에서 여러개의 파일을 동시에 업로드 할 수 있습니다.
for(MultipartFile uploadFile : uploadFiles){
//브라우저에 따라 업로드하는 파일의 이름은 전체경로일 수도 있고(Internet Explorer),
//단순히 파일의 이름만을 의미할 수도 있습니다.(chrome browser)
String originalName = uploadFile.getOriginalFilename();//파일명:모든 경로를 포함한 파일이름
String fileName = originalName.subString(originalName.lastIndexOf("//")+1);
//예를 들어 getOriginalFileName()을 해서 나온 값이 /Users/Document/bootEx 이라고 한다면
//"마지막으로온 "/"부분으로부터 +1 해준 부분부터 출력하겠습니다." 라는 뜻입니다.따라서 bootEx가 됩니다.
log.info("fileName" + fileName);
}//end for
}
}
@Controller
public class UploadTestController{
@GetMapping("/uploadEx")
public void uploadEx(){
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input name="uploadFiles" type="file" multiple>
<button class="uploadBtn">Upload</button>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrit="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous">
</script>
<script>
$('.uploadBtn').click(function( ) {
var formData = new FormData(); //FormData 객체 생성
var inputFile = $("input[type='file']");
//input 태그의 type이 file인것을 찾아서 inputFile이라는 변수로 지정
var files = inputFile[0].files;
//files : 선택한 모든 파일을 나열하는 FileList 객체입니다.
//multiple 특성을 지정하지 않닸다면 두 개 이상의 파일을 포함하지 않습니다.
for (var i = 0; i < files.length; i++) {
console.log(files[i]);
formData.append("uploadFiles", files[i]);//키,값으로 append
}
//실제 업로드 부분
//upload ajax
$.ajax({
url: '/uploadAjax', //경로
processData: false, //기본값은 true
//ajax 통신을 통해 데이터를 전송할 때, 기본적으로 key와 value값을 Query String으로 변환해서 보냅니다.
contentType: false, // multipart/form-data타입을 사용하기위해 false 로 지정합니다.
data: formData,
type: 'POST',
dataType:'json',
success: function(result){
//나중에 화면 처리
console.log(result);
},
error: function(jqXHR, textStatus, errorThrown){ //오류 메시지 판정
console.log(textStatus);
}
}); //$.ajax
});
</script>
</body>
</html>
@RestController
@Log4j2
public class UploadController{
<--------추가된 부분--------->
@Value("${part4.upload.path})//application.properties의 변수
private String uploadPath;
//@Value를 import할때 springframwork.beans.factory.annotation.Value;를 선택!
<--------추가된 부분--------->
@PostMapping("/uploadAjax)
public void uploadFile(MultipartFile[] uploadfiels){
//생략
}
@RestController
@Log4j2
public class UploadController {
@Value("${part4.upload.path}")
private String uploadPath;
@PostMapping("/uploadAjax")
public void uploadFile(MultipartFile[] uploadFiles) {
for(MultipartFile uploadFile : uploadFiles){
<---------추가----------->
if(uploadfile.getContentType().startWith("image") == false{
log.warn("this file is not image type");
return;
}
</---------추가-----------/>
String originalName = uploadFile.getOriginalFilename();
String fileName = originalName.subString(originalName.lastIndexOf("//")+1);
log.info("fileName" + fileName);
<---------추가----------->
//날짜 폴더 생성
String folderPath = makeFolder();
//UUID
String uuid = UUID.randomUUID().toString();
//저장할 파일 이름 중간에 "_"를 이용하여 구분
String saveName = uploadPath + File.separator + folderPath +File.separator + uuid + "_" + fileName;
Path savePath = Paths.get(saveName);
//Paths.get() 메서드는 특정 경로의 파일 정보를 가져옵니다.(경로 정의하기)
try{
uploadFile.transferTo(savePath)
//uploadFile에 파일을 업로드 하는 메서드 transferTo(file)
} catch (IOException e) {
e.printStackTrace();
//printStackTrace()를 호출하면 로그에 Stack trace가 출력됩니다.
}
</---------추가-----------/>
}//end for
}
<---------추가----------->
private String makeFolder(){
String str = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
//LocalDate를 문자열로 포멧
String folderPath = str.replace("/". File.separator);
//만약 Data 밑에 exam.jpg라는 파일을 원한다고 할때,
//윈도우는 "Data\\"eaxm.jpg", 리눅스는 "Data/exam.jpg"라고 씁니다.
//그러나 자바에서는 "Data" +File.separator + "exam.jpg" 라고 쓰면 됩니다.
//make folder ==================
File uploadPathFoler = new File(uploadPath, folderPath);
//File newFile= new File(dir,"파일명");
//->부모 디렉토리를 파라미터로 인스턴스 생성
if(uploadPathFolder.exists() == false){
uploadPathFoler.mkdirs();
//만약 uploadPathFolder가 존재하지않는다면 makeDirectory하라는 의미입니다.
//mkdir(): 디렉토리에 상위 디렉토리가 존재하지 않을경우에는 생성이 불가능한 함수
//mkdirs(): 디렉토리의 상위 디렉토리가 존재하지 않을 경우에는 상위 디렉토리까지 모두 생성하는 함수
}
return folderPath;
}
}
MIME 타입은 대소문자를 구분하지는 않지만 전통적으로 소문자로 쓰여집니다.