Spring

[Spring] 파일업로드 / 다운로드

sian han 2022. 6. 2. 23:42

 

※ 파일업로드 처리

 

· enctype 속성을 "multipart/form-data" 로 설정 / method = "post"

· <input type = "file" > 태그 사용

<form name=“frm” method=“post” enctype=“multipart/form-data”>
    <input type=“file” name=“filename”>
</form>

 

· 스프링은 Multipart 지원 기능을 제공하고 있기 때문에, 이 기능을 이용하면

  추가적인 처리없이 Multipart 형식으로 전송된 파라미터와 파일 정보를 쉽게 구할 수 있다

     - 이 기능을 이용하려면 MultipartResolver 를 스프링 설정 파일에 등록해 주어야 함

 

 

 

 MultipartResolver 

  : Multipart 형식으로 데이터가 전송된 경우, 해당 데이터를 스프링 MVC에서 사용할 수 있도록 변환해줌

 

MultipartResolver 를 사용하기 위해선 설정파일에 등록해줘야 함

  ▽ MvcConfiguration.java

@Bean
	public CommonsMultipartResolver multipartResolver() {
		CommonsMultipartResolver multipartResolver 
			= new CommonsMultipartResolver();
		multipartResolver.setDefaultEncoding("UTF-8"); // 파일 인코딩 설정
		multipartResolver.setMaxUploadSizePerFile(5 * 1024 * 1024); // 파일당 업로드 크기 제한 (5MB)
		return multipartResolver;
	}

   - 스프링이 기본으로 제공하는 MultipartResolver는 CommonsMultipartResolver 이다

   - CommonsMultipartResolver를 MultipartResolver 로 사용하려면

     빈 이름으로 "multipartResolver"를 사용해서 등록하면 됨

 

  - setMaxUploadSizePerFile : 메모리에 보관할 수 있는 최대 바이트크기. 기본 10240 바이트

  - setDefaultEncoding : 요청을 파싱할 때 사용할 캐릭터인코딩

 

 

 

 

 

▶ 파일 받아오는 3가지방법 (업로드 파일 접근)

 

 

[1] @RequestParam 어노테이션이 적용된 MultipartFile 타입의 파라미터를 사용하는 것

 

  - 파일 업로드

form action="submitReport1.do" method="post" enctype="multipart/form-data">
    학번: <input type="text" name="studentNumber" />
    <br/>
        리포트파일: <input type="file" name="report" />
    <br/>
    <input type="submit" />
</form>

   = > 파일은 report 파라미터를 통해서 전달됨 , 이 경우 아래와 같이

         @RequestParam 어노테이션과 MultipartFile 타입의 파라미터를 이용해서 업로드 파일 데이터를 전달받을 수 있다

 

 

@Controller
public class ReportSubmissionController {
    @RequestMapping(value = "/report/submitReport1.do", method = RequestMethod.POST)
    public String submitReport1(
        @RequestParam("studentNumber") String studentNumber,
        @RequestParam("report") MultipartFile report) { **
    //MultipartFile이 제공하는 메서드를 이용해서 업로드 데이터 접근
    printInfo(studentNumber, report);
    return "report/submissionComplete";
}

  = > MultipartFile 인터페이스를 이용해서 

        파일의 이름, 실제 데이터, 파일 크기 등을 구할 수 있다.

 

 

 

 

 

[2] 커맨더(commander) 객체를 이용해서 접근한다 (model attribute)

  - 커맨드 클래스에 파라미터와 동일한 이름의 MultipartFile 타입 프로퍼티를 추가해주면 됨

import org.springframework.web.multipart.MultipartFile;
    public class ReportCommand {
            private String studentNumber;
            private MultipartFile report;
            public String getStudentNumber() {
            return studentNumber;
        }
        public void setStudentNumber(String studentNumber) {
        	this.studentNumber = studentNumber;
        }
        public MultipartFile getReport() {
      	  	return report;
        }
        public void setReport(MultipartFile report) {
        	this.report = report;
    }
}
@Controller
public class ReportSubmissionController {
    @RequestMapping(value = "/report/submitReport3.do", method = RequestMethod.POST)
    public String submitReport3(@ModelAttribute ReportCommand reportCommand) {
    printInfo(reportCommand.getStudentNumber(), reportCommand.getReport());
    return "report/submissionComplete";
}

 

 

 

 

[3] MultipartHttpServletRequest를 이용한 업로드 파일 접근

     - MultipartHttpServletRequest 인터페이스 = 스프링이 제공하는 인터페이스

 

  ▷ MultipartFile getFile(String name) - 파라미터이름이 name 인 업로드 파일정보를 구한다 (하나업로드)

@Controller
public class ReportSubmissionController {
@RequestMapping(value = "/report/submitReport2.do", method = RequestMethod.POST)
    public String submitReport2(MultipartHttpServletRequest request) {
        String studentNumber = request.getParameter("studentNumber");
        MultipartFile report = request.getFile("report"); //요깅
        printInfo(studentNumber, report);
        return "report/submissionComplete";
}

 

 

  - FileUploadUtil.java

MultipartHttpServletRequest 인터페이스는 실제로는 어떤 메서드도 선언하고 있지 않으며,

HttpServletRequest 인터페이스와 MultipartRequest 인터페이스를 상속받고 있다

따라서 이런식으로 다운캐스팅 가능

public List<Map<String, Object>> fileUpload(HttpServletRequest request, int uploadFlag) 
	throws IllegalStateException, IOException {
		MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest)request;

 

 

 

 

MultipartHttpServletRequest 인터페이스의 주요메서드

      (다중파일업로드 방법) 

 

· Map<string, multipartfile=""> getFileMap()

    - key = 파라미터 이름 , value = 파라미터에 해당하는 파일정보 

     의 Map을 구한다

    - for 문 못돌림. iterator 를 써야함</string,>

    - input type file 을 여러개 사용한다

 

·  List<MultipartFile> getFiles(String)

    - 파라미터 이름이 name 인 업로드 파일 정보 목록을 구한다

    - input에 multiple ="multiple" 넣기

    - for 문 돌리기

 

 

▽ FileUploadUtil.java

@Component
public class FileUploadUtil {
	private static final Logger logger
	=LoggerFactory.getLogger(FileUploadUtil.class);

	public List<Map<String, Object>> fileUpload(HttpServletRequest request,
			int uploadFlag) 
					throws IllegalStateException, IOException {
		MultipartHttpServletRequest multiRequest 
		= (MultipartHttpServletRequest)request;

		Map<String, MultipartFile> fileMap=multiRequest.getFileMap();

		//업로드 파일 정보 저장할 List 선언
		List<Map<String, Object>> list = new ArrayList<>();

		Iterator<String> keyIter=fileMap.keySet().iterator();
		while(keyIter.hasNext()) {
			String key=keyIter.next();
			MultipartFile tempFile = fileMap.get(key); **
			//=> 업로드된 파일을 임시파일 형태로 제공

			if(!tempFile.isEmpty()) {
				long fileSize=tempFile.getSize(); //파일 크기
				String oName = tempFile.getOriginalFilename(); //원래 파일명

				//변경된 파일이름 구하기
				String fileName = getUniqueFileName(oName);

				//파일 업로드 처리
				//업로드할 폴더 구하기
				String uploadPath 
				= getUploadPath(request, uploadFlag);
				File file = new File(uploadPath, fileName); 
				tempFile.transferTo(file);

				//업로드된 파일 정보 저장
				//[1] Map에 저장
				Map<String, Object> resultMap = new HashMap<>();
				resultMap.put("fileName", fileName);
				resultMap.put("fileSize", fileSize);
				resultMap.put("originalFileName", oName);

				//[2] 여러 개의 Map을 List에 저장
				list.add(resultMap);
			}//if
		}//while

		return list;
	}

 

 

▲ MultipartFile 인터페이스

  - MultipartFile 인터페이스는 업로드 한 파일 정보 및 파일 데이터를 표현하기 위한 용도로 사용됨

 

 

 

 

 

▷MultipartFile 인터페이스의 주요 메서드

 

·  MultipartFile.transferTo()

       - 업로드한 파일 데이터를 특정 파일로 저장하고 싶다면 MultipartFile.transferTo() 메서드를 사용하는 것이 편리함

       - 업로드한 파일 데이터를 지정한 파일에 저장한다

 

·  getName()

       - 파라미터 이름을 구한다

 

·  getOriginalFilename()

       - 업로드한 파일의 이름을 구한다

 

·  getSize()

       - 업로드한 파일의 크기를 구한다

 

 

 

 

 

▶ 같은 파일명이 없도록 [파일명 + 년월일시분초밀리초] 로 파일명 지정하기

    - FileUploadUtil.java

public String getUniqueFileName(String fileName) {
		//파일명이 중복될 경우 파일이름 변경하기
		//파일명에 현재시간(년월일시분초밀리초)을 붙여서 변경된 파일이름 구하기
		//a.txt => a_20220602113820123.txt

		//순수 파일명만 구하기 => a
		int idx = fileName.lastIndexOf(".");
		String fileNm = fileName.substring(0,idx);  //a

		//확장자 구하기
		String ext = fileName.substring(idx); // .txt

		//변경된 파일이름
		Date d = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		String today = sdf.format(d);

		String result = fileNm + "_" + today + ext;
		logger.info("변경된 파일명 : {}", result);

		return result;
	}

 

 

 


 

 

 

 

※ 파일 다운로드 처리

 

 

 

▶ 뷰를 결정하는 방법 2가지

[1] 우선순위를 0을 줌으로서 BeanNameViewResolver에서 먼저 찾고

     없으면 InternalResourceViewResolver에서 찾게 됨

<beans:bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <beans:property name="order" value="0" />
</beans:bean>

  = > beanName이 String 타입으로 return 되었을때 이 BeanNameViewResolver가 받게 됨

 

 

BeansNameViewResolver 우선 빈이름으로 결정하고, 빈이름과같은게 없다면

[2] 앞뒤로 prefix suffix를 붙여서 결정한다

 

 

 

 


 

 

@RequiredArgsConstructor 생성자주입

final 붙은 애의 생성자를 만들어줌

 

dao 에 붙이는거 @Repository

@Service

controller에 붙이는거 @controller

@Component - 객체생성해줘 ~ dao도 service도 controller 도 아닐때 이거 씀

 


 

정리

 

  1. <input type=file/>로 파일을 받을 수 있다. 이때 form에는 enctype="multipart/form-data" 속성이 있어야 한다.
    • multiple="multiple"가 있으면 파일 여러개를 한번에 업로드 할 수 있다.
  2. 스프링에선 @RequestParam또는 @ModelAttribute으로 MultipartFile file을 받을 수 있다.
    • multiple="multiple"이면 List<MultipartFile file>로 받으면 된다.
  3. file.transferTo(new File("PATH") 을 이용해 파일을 저장할 수 있다.
    • 이때 사용자가 업로드한 파일명과 서버 내부에서 저장하는 파일명은 서로 다르게 저장한다. (그니깐 이 두개를 연결해주는 DB테이블이 필요하다.)
    • 사용자가 업로드한 파일명은 file.getOriginalFilename() 으로 받을 수 있다.
    • 서버 내부에서 저장하는 파일명은 절대 겹치게 설정해선 안된다.(UUID 등을 사용하자.)
  4. 이미지를 HTML에 보여줄 땐 별도의 컨트롤러가 필요하다.
    • new UrlResource("file:" + file.getFullPath(filename));을 이용해 Resource를 리턴하자.
    • html에선 <img src=~~>로 컨트롤러에서 넘어오는 Resource를 보여줄 수 있다.
  5. 파일 다운 링크를 만들기 위해서도 별도의 컨트롤러가 필요하다.
    • Response Header에 contentDisposition를 넣어줘야 한다.
    • body값으론 Resource를 넣어주면 된다.