개발 환경
- IDE : Spring Tool Suite 3.3.0.RELEASE
- Spring Framework 3.1.1 (Web MVC 포함)
- OS : Max OSX 10.8.5
연습으로 해본 이미지 업로드 프로젝트입니다. 잘못된 내용이 있거나 개선할 내용이 있으면 댓글 남겨주시면 배우도록 하겠습니다 :)
먼저 STS에서 Spring MVC Web 프로젝트를 하나 만듭니다.
프로젝트 탐색기에 보이는 프로젝트의 모습입니다. 위 단계까지 따라오셨으면 아래에 보이는 파일 중에 없는 것 몇가지가 있습니다. 이 글을 끝까지 따라가면 보이는 프로젝트 파일의 모습입니다.
예제 프로젝트에서는 패키지를 따로 나누지 않고 통패키지(..)에 모든 클래스 파일을 담았습니다.
먼저 servlet-context.xml 파일에 Multipart 사용을 위해서 멀티파트 리졸버를 추가합니다.
<!-- Multipart를 받기 위한 리졸버 추가 -->
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
MultipartResolver를 사용하게 되면 두개의 라이브러리가 추가적으로 필요합니다. Commons File Upload와 IO입니다.
메이븐을 사용한다면 의존 라이브러리를 추가해주고, 그 외 빌드 툴이나, 혹은 사용하지 않는다면 환경에 맞게, 두가지의 라이브러리를 추가해줍니다.
<!-- Apache Commons File Upload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2</version>
</dependency>
<!-- Apache Commons IO 추가 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency><
이미지 업로드 컨트롤러 클래스 내용입니다.
이미지 업로드를 위한 폼 페이지와 결과 페이지 및 이미지를 얻기 위한 URL을 매핑합니다.
package com.nnoco.example;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class ImageUploadController {
/*
* ImageView는 파일 시스템에 있는 이미지 파일을 응답으로 돌려주는 역할을 합니다.
* 뒷 부분에서 ImageView 클래스를 작성하게 됩니다.
*/
@Resource(name="imageView") ImageView imageView;
/**
* 이미지를 관리하는 서비스 계층 클래스입니다. 예제에서는 디비를 사용하긴 버거워지므로
* 서비스 클래스를 따라하는 모양만 서비스인 클래스입니다.
*/
@Autowired ImageService imageService;
/**
* 이미지 업로드를 위한 페이지 매핑
*/
@RequestMapping("/uploadPage")
private String uploadView() {
return "upload";
}
/**
* 이미지 업로드 페이지의 폼에서 전송 시 받게 되는 메서드
*/
@RequestMapping(value="/upload", method=RequestMethod.POST)
private String upload(@RequestParam MultipartFile imageFile, ModelMap modelMap) {
ImageFile fileInfo = imageService.save(imageFile);
modelMap.put("imageFile", fileInfo);
return "uploadComplete";
}
@RequestMapping("/image/{imageId}")
private ImageView getImage(@PathVariable String imageId, ModelMap modelMap) {
ImageFile imageFile = imageService.get(imageId);
modelMap.put("imageFile", imageFile);
return imageView;
}
}
업로드한 이미지 정보를 담고있는 ImageFile 클래스입니다.
package com.nnoco.example;
public class ImageFile {
/**
* 업로드한 이미지 파일이 저장될 경로
*/
public static final String IMAGE_DIR = "/web/upload_images/";
private String id;
private String contentType;
private int contentLength;
private String fileName;
public ImageFile(String id, String contentType, int contentLength,
String fileName) {
this.id = id;
this.contentType = contentType;
this.contentLength = contentLength;
this.fileName = fileName;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public int getContentLength() {
return contentLength;
}
public void setContentLength(int contentLength) {
this.contentLength = contentLength;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
이미지 뷰 클래스입니다.
package com.nnoco.example;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.AbstractView;
@Component("imageView")
public class ImageView extends AbstractView{
@Override
protected void renderMergedOutputModel(Map model,
HttpServletRequest req, HttpServletResponse res) throws Exception {
ImageFile imageFile = (ImageFile)model.get("imageFile");
// 응답 메시지에 파일의 길이를 넘겨줍니다.
res.setContentLength(imageFile.getContentLength());
// 응답의 타입이 이미지임을 알려줍니다.
res.setContentType(imageFile.getContentType());
// 파일로부터 byte를 읽어옵니다.
byte[] bytes = readFile(imageFile.getFileName());
write(res, bytes);
}
/**
* 파일로부터 byte 배열 읽어오기
*/
private byte[] readFile(String fileName) throws IOException {
String path = ImageFile.IMAGE_DIR + fileName;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
int length = bis.available();
byte[] bytes = new byte[length];
bis.read(bytes);
bis.close();
return bytes;
}
/**
* 응답 OutputStream에 파일 내용 쓰기
*/
private void write(HttpServletResponse res, byte[] bytes) throws IOException {
OutputStream output = res.getOutputStream();
output.write(bytes);
output.flush();
}
}
이미지 서비스 클래스입니다.
모양만 서비스이고, 구현은 전혀 아닙니다. 맵 자료구조를 사용해서 DB를 대신했습니다.
package com.nnoco.example;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class ImageService {
private Map imageFilesMap;
public ImageService() {
init();
}
/**
* 초기화
*/
private void init() {
imageFilesMap = new HashMap();
}
/**
* ID로 이미지 파일 가져오기
*/
public ImageFile get(String id) {
return imageFilesMap.get(id);
}
/**
* Multipart File을 파일로 저장하고 DB(를 빙자한 맵)에 업로드 파일 정보 저장, 실패하는 경우 null리
*/
public ImageFile save(MultipartFile multipartFile) {
// UUID로 유일할 것 같은 값 생성.. 낮은 확률로 중복 가능성이 있음
String genId = UUID.randomUUID().toString();
ImageFile imageFile = null;
try {
String savedFileName = saveToFile(multipartFile, genId);
imageFile = new ImageFile(genId,
multipartFile.getContentType(),
(int)multipartFile.getSize(),
savedFileName);
imageFilesMap.put(genId, imageFile);
} catch (IOException e) {
e.printStackTrace();
}
return imageFile;
}
/**
* Multipart File의 내용을 파일로 저장, 저장 후 저장된 파일 이름을 반환
*/
private String saveToFile(MultipartFile src, String id) throws IOException {
String fileName = src.getOriginalFilename();
byte[] bytes = src.getBytes();
String saveFileName = id + "." + getExtension(fileName);
String savePath = ImageFile.IMAGE_DIR + saveFileName;
/* 파일 쓰기 */
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(savePath));
bos.write(bytes);
bos.flush();
bos.close();
return saveFileName;
}
/**
* 파일이름으로부터 확장자를 반환하는 메서드
* 파일이름에 확장자 구분을 위한 . 문자가 없거나. 가장 끝에 있는 경우는 빈문자열 ""을 리턴
*/
private String getExtension(String fileName) {
int dotPosition = fileName.lastIndexOf('.');
if (-1 != dotPosition && fileName.length() - 1 > dotPosition) {
return fileName.substring(dotPosition + 1);
} else {
return "";
}
}
}
파일 업로드를 위한 JSP 페이지와 결과 확인 페이지입니다.
upload.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>이미지 업로드</title>
</head>
<body>
<form action="./upload" method="post" enctype="multipart/form-data">
<input type="file" name="imageFile"><br>
<input type="submit" value="전송">
</form>
</body>
</html>
uploadComplete.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 http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>업로드 결과 페이지</title>
<style type="text/css">
.failed {
color: red;
font-style: bold;
font-size:18pt;
}
</style>
</head>
<body>
<c:choose>
<c:when test="${imageFile != null }">
파일 업로드 완료
<ul>
<li>파일 ID : ${imageFile.id }</li>
<li>저장된 파일 이름 : ${imageFile.fileName }</li>
<li>파일 길이 : ${imageFile.contentLength }</li>
<li>MIME 타입 : ${imageFile.contentType }</li>
</ul>
<img src="${pageContext.request.contextPath}/image/${imageFile.id}">
</c:when>
<c:otherwise>
<span class="failed">파일 업로드 실패</span>
</c:otherwise>
</c:choose>
</body>
</html>
여기까지 필요한 설정과 파일을 추가하고, 서버에 올려 접속을 하면 아래와 같은 아주 간단한 폼과 함께 파일을 업로드 해볼 수 있습니다.
이미지 업로드라고 하긴 했지만 사실 이미지 업로드를 위해 한 일은 없고, 파일 업로드를 하는 작업만 한 것 같네요.
위 예제 프로젝트는 아래 파일을 다운로드하시어 살펴보실 수 있습니다.
'밤을 지새다 > 배운 것을 기록하라' 카테고리의 다른 글
boilerpipe를 이용한 기사 본문 내용 추출 (0) | 2015.05.20 |
---|---|
콘솔 출력을 JSON 포맷의 Pretty Print 적용하기 (0) | 2014.09.23 |
RSA 비대칭키 암호화 알고리즘 (0) | 2013.10.09 |