[Spring] File Upload & Download

young-gue Park·2023년 10월 9일
0

Spring

목록 보기
5/14
post-thumbnail

⚡ File Upload & Download


📌 File Upload

💡 MVN 레파지토리에서 File Upload를 검색하면 뜨는 Apache Commons FileUpload 1.4를 활용한다. (dependency 복사 후 pom.xml에 붙여넣기하여 의존성 추가)

💡 이후에 servlet-context.xmlmultipartResolver를 bean으로 등록한다.

<beans:bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
	id="multipartResolver">
	<!-- 현재 value는 10MB 정도이며 무제한을 쓰려면 -1, 일반적으로는 쓰지 않음 -->
	<beans:property name="maxUploadSize" value="10485760" />
	<beans:property name="defaultEncoding" value="UTF-8" />
</beans:bean>
  • CommonsMultipartResolver 속성

    1) defaultEncoding(String): 요청에서 사용할 기본 문자 인코딩 방식을 설정한다.

    2) maxUploadSize(long): 업로드 가능 최대 크기(바이트)를 설정한다.

    3) maxInMemorySize(int): 디스크에 쓰기 전에 메모리에 허용되는 최대 크기(바이트)를 설정한다.

    4) maxUploadSizePerFile(long): 파일 당 최대 크기를 설정한다.

🔷 파일 업로드를 위해 form 태그에서 methodpost 방식으로, enctype"multipart/form-data"로 지정해 놓는 것이 필수이다.

💻 regist.jsp

💡 맨 처음 프로젝트를 시작할 때 보여줄 뷰인 home.jsp에는 단순히 regist 요청을 보낼 수 있게하는 a 태그 하나만 있다. 그래서 이곳에는 딱히 언급하지 않겠다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일 업로드</title>
</head>
<body>
	<h2>파일 업로드</h2>
	<form action="upload" method="POST" enctype="multipart/form-data">
		<input type="file" name="upload_file"> 
		<input type="submit" value="파일등록">
      	<!-- form 태그 안에서는 submit과 똑같이 작동한다. -->
		<button>제출</button>
	</form>

	<!-- 여러개의 파일 올리기, multiple 속성으로 설정해야한다. -->
	<form action="upload2" method="POST" enctype="multipart/form-data">
		<input type="file" name="upload_files" multiple="multiple"> 
		<input type="submit" value="파일등록">
	</form>



</body>
</html>

💻 MainController.java

package com.bzeromo.mvc.controller;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

/**
 * Handles requests for the application home page.
 */
@Controller
public class MainController {
	
	@Autowired
	private ServletContext servletContext;
	
    //리소스의 경로를 편하게 가져오는 방법
	@Autowired
	private ResourceLoader resLoader;
	
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home() {
		return "home";
	}
	
	
	@GetMapping("regist")
	public String registForm() {
		return "regist";
	}
	
	@PostMapping("upload")
	public String upload(MultipartFile upload_file, Model model) {
		// 파일이 실제로 저장될 위치를 가져온다.
		String uploadPath = servletContext.getRealPath("/upload");
		
		//만약에 등록하려고 하는 upload 폴더가 없다면 만든다.
		File folder = new File(uploadPath);//폴더
		if(!folder.exists()) //존재하지 않는다면
			folder.mkdir(); //폴더 생성
		
		//실제 파일이름을 가져온다.
		String fileName = upload_file.getOriginalFilename();
		File target = new File(uploadPath, fileName);
		//파일을 해당 폴더에 저장하기
		//저장하는 방법은 크게 2가지 
		//1. FileCopyUtiles (더 심플하다)
		//2. File 인스턴스를 직접 활용
		
		try {
			FileCopyUtils.copy(upload_file.getBytes(), target);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		model.addAttribute("fileName", fileName);
		
		return "result";
	}
	
    //여러 개의 파일 업로드하기, upload_files를 MultipartFile 배열로 받아야한다.
	@PostMapping("upload2")
	public String upload2(MultipartFile[] upload_files, Model model) throws IOException {
		
		//파일들의 이름을 저장할 리스트 생성 (임시)
		List<String> list = new ArrayList<>();
		
		//null이 아니면 파일들이 있다는 뜻이므로 업로드
		if(upload_files != null) {
			Resource res = resLoader.getResource("resources/upload");
			if(!res.getFile().exists())
				res.getFile().mkdir();
			
			//단순히 upload 폴더에 저장하는게 아니라 경로를 날짜별로 할건지 사람별로 할것인지에 대한 고민을 할 필요가 있다. 이름이 같은 파일이 들어오면 덮어쓰기 당하기 때문.
			
			for(MultipartFile mFile :upload_files) {
				//가짜 파일들 생략(내용이 없는 것들)
				if(mFile.getSize() > 0) {
					mFile.transferTo(new File(res.getFile(), mFile.getOriginalFilename()));
					list.add(mFile.getOriginalFilename());
				}
				
			}
		}
		
		model.addAttribute("list", list);
		
		
		return "result";
	}
}

💻 result.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>결과화면</title>
</head>
<body>
	<a href="/mvc/upload/${fileName}">${fileName}</a>
	<img src="${pageContext.request.contextPath}/upload/${fileName}">
		
	<c:forEach items="${list}" var="fileName">
		${fileName }
	</c:forEach>
</body>
</html>

❗ 첫 번째 a 태그를 눌러도 파일 사진 대신 오류가 뜬다면, servlet-context에서 upload 요청에 대해 upload에 해당하는 폴더로 따로 매핑해주어야 한다.
<resources mapping = "/upload/**" location="/upload/" />


📌 File Download

💡 servlet-context.xmlFileDownLoadView를 bean으로 등록한다.

  • 이 때, Bean 이름으로 뷰를 찾기 위해 BeanNameViewResolver를 등록한다.
<beans:bean class="com.bzeromo.mvc.view.FileDownLoadView" id="fileDownLoadView"/>
<beans:bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
	<beans:property name="order" value="0"></beans:property>
</beans:bean>

💻 FileDownLoadView.java

package com.bzeromo.mvc.view;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;

public class FileDownLoadView extends AbstractView {

	public FileDownLoadView() {
		setContentType("application/download; charset=UTF-8");
	}
	
	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		//////////////////////////////////////////////////////////////////////
		ServletContext ctx = getServletContext();
		String realPath = ctx.getRealPath("/upload");
		
		//다운로드 받을 파일의 경로가 다르면 위의 코드도 달라져야 함.
		
		Map<String, Object> fileInfo = (Map<String, Object>) model.get("downloadFile"); // 전송받은 모델(파일 정보)
        String fileName = (String) fileInfo.get("fileName");    // 파일 경로
        
        System.out.println(fileName);
        File file = new File(realPath, fileName);
		/////////////////////////////////////////////////////////////////////
        
        response.setContentType(getContentType());
        response.setContentLength((int) file.length());
        
        String header = request.getHeader("User-Agent");
        boolean isIE = header.indexOf("MSIE") > -1 || header.indexOf("Trident") > -1;
        String filename = null;
        // IE는 다르게 처리
        if (isIE) {
        	filename = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        } else {
            filename = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
        }
        response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\";");
        response.setHeader("Content-Transfer-Encoding", "binary");
        
        OutputStream out = response.getOutputStream();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            FileCopyUtils.copy(fis, out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(fis != null) {
                try { 
                    fis.close(); 
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        out.flush();
    }
}

🔷 MainController 하단에 해당 코드를 추가한다.

@GetMapping("download")
public String download(Model model, String fileName) {
		
	Map<String, Object> fileInfo = new HashMap<>();
		
	fileInfo.put("fileName", fileName);
		
	model.addAttribute("downloadFile", fileInfo);
	return "fileDownLoadView";
}

🔷 result.jsp 하단에 해당 코드를 추가한다.

<a href="download?fileName=${fileName }">${fileName }다운로드</a>

나의 휴일이 사라졌다!

profile
Hodie mihi, Cras tibi

0개의 댓글