파일 업로드에 필요한 라이브러리를 설치할 수 있도록 <dependency>
...
<!-- 파일 업로드 -->
<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>
...
저장하고 받아진거 확인
CommonsMultipartResolver 클래스를 multtipartResolver 빈으로 설정한다
...
<!-- multipartResolver 빈 설정 -->
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<beans:property name="maxUploadSize" value="52428800" />
<beans:property name="maxInMemorySize" value="10000000" />
<beans:property name="defaultEncoding" value="utf-8" />
</beans:bean>
...
파일 업로드 및 다운로드 기능 컨트롤러 구현을 위해 작성
파일을 다운로드하는 컨트롤러 클래스. 버퍼기능을 이용해 브라우저로 빠르게 이미지를 전송할 것이다
package com.spring.pro28;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class FileDownloadController {
// 저장할 파일 위치 지정
private static String CURR_IMAGE_REPO_PATH = "c://spring//image_repo";
@RequestMapping("/download")
// 다운로드할 이미지 파일이름을 param(전달)
protected void download(@RequestParam("imageFileName") String imageFileName,
HttpServletResponse response) throws Exception {
OutputStream out = response.getOutputStream();
String downFile = CURR_IMAGE_REPO_PATH + "\\" + imageFileName;
// 다운로드할 파일 객체 생성하기
File file = new File(downFile);
response.setHeader("Cache-Control", "no-cache");
// 헤더에 파일이름 설정
response.addHeader("Content-disposition", "attachment; fileName = " + imageFileName);
FileInputStream in = new FileInputStream(file);
byte[] buffer = new byte[1024 * 8];
// 버퍼를 이용해 한번에 8Kbyte 씩 브라우저로 전송한다
while (true) {
int count = in.read(buffer);
if(count == -1) {
break;
}
out.write(buffer, 0, count);
}
in.close();
out.close();
}
}
파일 업로드하는 부분
package com.spring.pro28;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class FileUploadController {
private static final String CURR_IMAGE_REPO_PATH = "c:\\spring\\image_repo";
// 업로드 창인 uploadForm.jsp를 반환할 것이다
@RequestMapping(value="/form")
public String form() {
return "uploadForm";
}
@RequestMapping(value = "/upload", method = RequestMethod.POST)
public ModelAndView upload(MultipartHttpServletRequest multipartRequest,
HttpServletResponse response) throws Exception {
multipartRequest.setCharacterEncoding("utf-8");
// 매개변수 정보와 파일 정보를 저장할 Map 생성
Map map = new HashMap();
Enumeration enu = multipartRequest.getParameterNames();
// 전송된 매개변수 값을 key/value로 map에 저장한다
while (enu.hasMoreElements()) {
String name = (String)enu.nextElement();
String value = multipartRequest.getParameter(name);
map.put(name, value);
}
// 파일을 업로드한 후 반환된 파일 이름이 저장되는 fileList를 다시 map에 저장할 것이다
List fileList = fileProcess(multipartRequest);
map.put("fileList", fileList);
ModelAndView mav = new ModelAndView();
// map을 결과창으로 포워딩
mav.addObject("map", map);
mav.setViewName("result");
return mav;
}
private List<String> fileProcess(MultipartHttpServletRequest multipartRequest) throws Exception{
List<String> fileList= new ArrayList<String>();
// 첨부된 파일 이름을 가져올 것이다
Iterator<String> fileNames = multipartRequest.getFileNames();
while(fileNames.hasNext()){
String fileName = fileNames.next();
// 파일 이름에 대한 MultipartFile 객체 가져오기
MultipartFile mFile = multipartRequest.getFile(fileName);
// 실제 파일 이름 가져오기
String originalFileName=mFile.getOriginalFilename();
// 파일 이름을 하나씩 fileList에 저장하기
fileList.add(originalFileName);
File file = new File(CURR_IMAGE_REPO_PATH +"\\"+ fileName);
// 첨부된 파일이 존재하는지 체크
if(mFile.getSize()!=0){
//경로상에 파일이 존재하지 않을 경우
if(! file.exists()) {
//경로에 해당하는 디렉토리를 만들어라
if(file.getParentFile().mkdirs()){
// 그 후에 파일을 생성해라
file.createNewFile();
}
}
// 임시로 저장된 mutilpartFile을 실제 파일로 저장
mFile.transferTo(new File(CURR_IMAGE_REPO_PATH +"\\"+ originalFileName)); //임시로 저장된 multipartFile을 실제 파일로 전송
}
}
// 첨부한 파일 이름이 저장된 fileList 반환해줘라
return fileList;
}
}
파일 업로드 창과 업로드한 파일을 표시할 결과창 준비
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR" isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<% request.setCharacterEncoding("UTF-8"); %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>파일 업로드하기</title>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
// 파일 업로드 name 값을 각각 다르게 하는 변수
var cnt = 1;
// 파일 추가를 클릭시 동적으로 파일 업로드를 추가할 것이라는 것
// name 속성의 값으로 'file'+cnt 를 설정함으로써 값을 다르게 해준다
function fn_addFile() {
$("#d_file").append("<br>"+"<input type='file' name='file"+cnt+"' />");
cnt++;
}
</script>
</head>
<body>
<h1>파일 업로드 하기</h1>
<!-- enctype : 파일 업로드시 반드시 mutilpart/form-data로 설정해야 한다 -->
<form method="post" action="${contextPath}/upload" enctype="multipart/form-data">
<label>아이디:</label>
<input type="text" name="id"><br>
<label>이름:</label>
<input type="text" name="name"><br>
<input type="button" value="파일추가" onClick="fn_addFile()"/><br>
<!-- 자바 스크립트를 이용해 <div> 태그안에 파일 업로드를 추가 -->
<div id="d_file">
</div>
<input type="submit" value="업로드"/>
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR" isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>결과 창</title>
</head>
<body>
<h1> 업로드가 완료되어습니다 </h1>
<label>아이디 : </label>
<!-- value="${map.id }" : map으로 넘어온 매개변수 값을 표시할 것이다 -->
<input type="text" name="id" value="${map.id }" readonly><br>
<label>이름:</label>
<input type="text" name="name" value="${map.name }" readonly><br>
<div class="result-images">
<!-- 업로드한 파일들을 forEach문을 이용해 <img> 태그에 표시 -->
<c:forEach var="imageFileName" items="${ map.fileList}" >
<img src="${pageContext.request.contextPath }/download?imageFileName=${imageFileName }">
<br><br><br>
</c:forEach>
</div> <p>
<a href='${pageContext.request.contextPath }/form'> 다시 업로드 하시겠습니까? </a> </p>
</body>
</html>
서버를 켜고 http://localhost:8700/pro28/form
를 요청하면 아이디/이름/파일추가가 뜬다. 그리고 파일 추가를 눌렀는데 안된다. 이건 uploadForm에서 스크립트 function fn_addFile()
이 부분이 잘 못된것이라 생각했고, 싹 지우고 다시 한자한자 쳐줬다. 그리고 다시 실행 -> 된다!
값을 넣고 업로드로 넘기면 (이미지를 한번에 여러개 여러개 넘기는 것도 가능하다)
이미지가 너무 커서 짤리는 데, 이런식으로 출력된다(오타가 있었네.. 기능이 오류없이 돌아간다는게 기뻐서 이제 봤다)
위처럼 입력한 아이디/이름, 이미지가 출력되고 하단에 다시 업로드 할 것인지 뜬다
누르면 다시 uploadForm으로 돌아간다
그리고 지정된 위치에 (FileDownloadController
에서 파일 위치를 적어줬다) 파일이 생성되고, 업로드한 이미지가 저장된 모습을 확인할 수 있었다
썸네일 이미지를 사용하면, 상품 목록 나열시 빠르게 이미지를 표시할 수 있다
썸네일 라이브러리 설정하기
...
<!-- 썸네일 이미지 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>
...
라이브러리가 추가된거 확인
package com.spring.pro28.ex02;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import net.coobird.thumbnailator.Thumbnailator;
import net.coobird.thumbnailator.Thumbnails;
@Controller
public class FileDownloadController {
private static String CURR_IMAGE_REPO_PATH = "c://spring//image_repo";
@RequestMapping("/download")
protected void download(@RequestParam("imageFileName") String imageFileName,
HttpServletResponse response) throws Exception {
OutputStream out = response.getOutputStream();
String filePath = CURR_IMAGE_REPO_PATH + "\\" + imageFileName;
File image = new File(filePath);
// 확장자를 제외한 원본 이미지 파일의 이름 가져오기
// .jpg 나 .png 이런 식이니까 . 이전의 이름 가져오기
int lastindex = imageFileName.lastIndexOf(".");
String fileName = imageFileName.substring(0, lastindex);
// 원본 이미지 파일 이름과
// 같은 이름의 썸네일 파일에 대한 File 객체 생성하기
File thumbnail = new File(CURR_IMAGE_REPO_PATH + "\\" + "Thumbnailator" + "\\" + fileName+ ".png");
// 원본 이미지 파일을 가로세로 50픽셀인 png형식으로 재 생성한다
if (image.exists()) {
thumbnail.getParentFile().mkdirs();
Thumbnails.of(image).size(50, 50).outputFormat("png").toFile(thumbnail);
}
// 생성된 썸네일 파일을 브라우저로 전송한다
FileInputStream in = new FileInputStream(thumbnail);
byte[] buffer = new byte[1024 * 8];
while (true) {
int cnt = in.read(buffer);
if (cnt == -1) {
break;
}
out.write(buffer, 0, cnt);
}
in.close();
out.close();
}
}
http://localhost:8700/pro28/form
으로 요청해서 하는데, 우리는 이미 ex02 파일 밖에 (실습1에 사용한) FileDownController 가 있기 때문에 그대로 실행하면 아까와 똑같은 결과가 나올 것이다. 따라서 먼저 사용한 FileDownController 의 @Controller
는 지워주고(또는 주석처리 해주고) ex02의 FileDownController는 @Controller
가 제대로 붙어있는지 확인한다.
실행하면 아까와 같이 입력창이 나온다
값을 넣고 업로드로 넘겨주면 이번에는 이미지가 작아진 상태로 뜨는 것을 확인할 수 있다.
그리고 설정한 위치에 파일 생성 후 변경된 썸네일 사진들이 들어가 있는 모습도 확인 가능했다
원본 이미지를 바로 출력하려면 FileUploadcontroller에서 아래와 같이 변경하면된다
...
File thumbnail = new File(CURR_IMAGE_REPO_PATH + "\\" + "Thumbnailator" + "\\" + fileName+ ".png");
// 원본 이미지 파일을 가로세로 50픽셀인 png형식으로 재 생성한다
if (image.exists()) {
// 원본 이미지에 대한 썸네일 이미지를 생성 한 후 OutputStream 객체에 바로 할당한다
Thumbnails.of(image).size(50, 50).outputFormat("png").toOutputStream(out);
} else { return; }
// 썸네일 이미지를 OutputStream 객체를 이용해 브라우저로 바로 전송
byte[] buffer = new byte[1024 * 8];
out.write(buffer);
out.close();
}
입력창에 값을 넣고, 업로드로 보내면
이미지가 잘 출력되는데.. 이름 값은 왜 깨지는 것이지
아까 이미지가 저장되었던 파일에 가도 새롭게 저장된 파일은 없다는 걸 확인할 수 있었다