BACK_END/Spring 공부

Spring - 파일 업로드 & 다운로드

harry595 2021. 3. 10. 01:59

multipart란

- 웹 클라이언트가 요청을 보낼 때 http 프로토콜의 바디 부분에 데이터를 여러 부분으로 나눠서 보내는 것

- 보통 파일을 전송할 때 사용한다.

 

HttpServletRequest는 파일 업로드 즉 Multipart 데이터를 쉽게 처리하는 메소드를 제공하지 않는다.

-> 이로 인해  별도의 라이브러리를 사용해야하는데 대표적으로 아파치의 commons-fileupload가 있다.

 

Spring MVC의 파일 업로드

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

 

DispatcherServlet은 준비과정에서 "multipart/form-data"가 요청으로 올 경우 MultipartResolver를 사용

(WebMvcContextConfiguration.java에 추가)

@Bean
public MultipartResolver multipartResolver() {
org.springframework.web.multipart.commons.CommonsMultipartResolver multipartResolver = new org.springframework.web.multipart.commons.CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10485760); // 1024 * 1024 * 10
return multipartResolver;
}

파일 업로드시에는 form 태그에 enctype설정이 되어 있어야 하고 post 방식이어야 함

타입이 파일인 인풋 태그가 여러개 있고 네임 속성의 값이 같으면 파일이 배열 형태로 컨트롤러에 저장

<form method="post" action="/upload" enctype="multipart/form-data">
	......
	<input type="file" name="file">
	<input type="submit">
</form>

 

Controller에서의 업로드 처리

- @PostMapping이 사용되야 함

- 업로드 파일이 하나일 경우 @RequestParam("file") MultipartFile file

- 업로드 파일이 여러 개일 경우 @RequestParam("file") MultipartFile[] files

- MultipartFile의 메소드를 이용해서 파일 이름, 파일 크기 등을 구하고 InputStream을 얻어 파일을 서버에 저장합니다.

 

Controller에서의 다운로드 처리

- 파일 다운로드와 관련된 헤더 정보를 출력

response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Type", contentType);
response.setHeader("Content-Length", fileLength;
response.setHeader("Pragma", "no-cache;");
response.setHeader("Expires", "-1;");

 

 

- 파일을 읽어 HttpServletResponse의 OutputStream으로 출력

 

FileController 예시

package kr.or.connect.guestbook.controller;

import java.io.FileOutputStream;
import java.io.InputStream;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
public class FileController {

    // get방식으로 요청이 올 경우 업로드 폼을 보여줍니다.
	@GetMapping("/uploadform")
	public String uploadform() {
		return "uploadform";
	}
	
	@PostMapping("/upload")
	public String upload(@RequestParam("file") MultipartFile file) {
		
		System.out.println("파일 이름 : " + file.getOriginalFilename());
		System.out.println("파일 크기 : " + file.getSize());
		
        try(
                // 맥일 경우 
                //FileOutputStream fos = new FileOutputStream("/tmp/" + file.getOriginalFilename());
                // 윈도우일 경우
                FileOutputStream fos = new FileOutputStream("c:/tmp/" + file.getOriginalFilename());
                InputStream is = file.getInputStream();
        ){
        	    int readCount = 0;
        	    byte[] buffer = new byte[1024];
            while((readCount = is.read(buffer)) != -1){
                fos.write(buffer,0,readCount);
            }
        }catch(Exception ex){
            throw new RuntimeException("file Save Error");
        }
		
		
		return "uploadok";
	}
    
    @GetMapping("/download")
	public void download(HttpServletResponse response) {

        // 직접 파일 정보를 변수에 저장해 놨지만, 이 부분이 db에서 읽어왔다고 가정한다.
		String fileName = "connect.png";
		String saveFileName = "c:/tmp/connect.png"; // 맥일 경우 "/tmp/connect.png" 로 수정
		String contentType = "image/png";
		int fileLength = 1116303; // connect.png 파일의 크기와 같아야 합니다. 파일의 크기를 꼭 확인해주세요.
                //파일의 크기와 같지 않을 경우 프로그램이 멈추지 않고 계속 실행되거나, 잘못된 정보가 다운로드 될 수 있습니다.
                // 위의 파일크기를 구하는 부분은 다음과 같은 방식으로 수정되는 것이 좋습니다. 코드를 간단하게 구현하기 위해서 직접 length를 입력하였습니다.
                // File file = new File(saveFileName);
                // long fileLength = file.length();
		
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
        response.setHeader("Content-Transfer-Encoding", "binary");
        response.setHeader("Content-Type", contentType);
        response.setHeader("Content-Length", "" + fileLength);
        response.setHeader("Pragma", "no-cache;");
        response.setHeader("Expires", "-1;");
        
        try(
                FileInputStream fis = new FileInputStream(saveFileName);
                OutputStream out = response.getOutputStream();
        ){
        	    int readCount = 0;
        	    byte[] buffer = new byte[1024];
            while((readCount = fis.read(buffer)) != -1){
            		out.write(buffer,0,readCount);
            }
        }catch(Exception ex){
            throw new RuntimeException("file Save Error");
        }
	}
}