💡 MVN 레파지토리에서 File Upload
를 검색하면 뜨는 Apache Commons FileUpload 1.4
를 활용한다. (dependency 복사 후 pom.xml에 붙여넣기하여 의존성 추가)
💡 이후에 servlet-context.xml
에 multipartResolver
를 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 태그에서 method
를 post
방식으로, 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/" />
💡 servlet-context.xml
에 FileDownLoadView
를 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>
나의 휴일이 사라졌다!