2017 - 12 - 07 (목)
이미지 업로드하기
1 . pom.xml 에서 라이브러리를 추가한다. ( spring version : 4.1.7 )
<!-- 이미지 업로드 -->
<!-- https://mvnrepository.com/artifact/org.imgscalr/imgscalr-lib--> <dependency>
<groupId>org.imgscalr</groupId>
<artifactId>imgscalr-lib</artifactId>
<version>4.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>
2 . servlet-context.xml 에 multipartResolver 빈 등록 : maxUploadSize 의 경우 10485760 으로 정했는데 이는 10MB 정도를 의미한다. 해당 빈을 통해 multipart타입의 데이터 처리를 가능하게 한다.
<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<beans:property name="maxUploadSize" value="10485760"></beans:property>
</beans:bean>
3 . 한글이름의 이미지파일이 올라가기 위해서 web.xml 에 인코딩 필터를 추가한다.
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4 . servlet-context.xml 에 이미지 파일폴더 생성 후 저장할 경로를 지정해준다. 실제로 데이터베이스에 저장할 값은 이미지 경로이며 이미지 파일은 uploadPath 빈에서 지정해준 경로에 저장이된다.
<beans:bean id = "uploadPath" class="java.lang.String">
<beans:constructor-arg value="C:\WorkSpace_Spring\login\src\main\webapp\resources\profileImage">
</beans:constructor-arg>
</beans:bean>
5 . uploadController 생성
@Controller
public class UploadController {
private static final Logger logger = LoggerFactory.getLogger(UploadController.class);
@Resource(name = "uploadPath")
private String uploadPath;
@RequestMapping(value = "/uploadForm", method = RequestMethod.GET)
public void uploadForm() throws Exception {
}
@RequestMapping(value = "/uploadForm", method = RequestMethod.POST)
public String uploadForm(MultipartFile file, Model model) throws Exception {
logger.info("originalName: " + file.getOriginalFilename());
logger.info("size: " + file.getSize());
logger.info("contentType: " + file.getContentType());
String savedName = uploadFile(file.getOriginalFilename(), file.getBytes());
model.addAttribute("savedName", savedName);
return "uploadResult";
}
private String uploadFile(String originalName, byte[] fileData) throws Exception {
UUID uid = UUID.randomUUID();
String savedName = uid.toString() + "_" + originalName;
File target = new File(uploadPath, savedName);
FileCopyUtils.copy(fileData, target);
return savedName;
}
}
6 . jsp 생성후 테스트 진행 : 스크립트를 보면 profileImg(이미지) 클릭시 형식의 버튼이 클릭 되도록 한다. 버튼을 클릭하여 파일 선택시 fileChange(e) 함수가 실행되고 서버로 비동기 요청을 보내게된다.
<script>
$(document).ready(function(){
$("#profileImg").click(function(){
$("#input_img").click() ;
})
})
</script>
<script>
var sel_file;
$(document).ready(function() {
$("#input_img").on("change", fileChange);
});
function fileChange(e) {
e.preventDefault();
var files = e.target.files;
var filesArr = Array.prototype.slice.call(files);
filesArr.forEach(function(f) {
if(!f.type.match("image.*")) {
alert("확장자는 이미지 확장자만 가능합니다.");
return;
}
sel_file = f;
var reader = new FileReader();
reader.onload = function(e) {
$("#profileImg").attr("src", e.target.result);
$("#profileImg").css("height", "100px")
}
reader.readAsDataURL(f);
});
var file = files[0]
console.log(file)
var formData = new FormData();
formData.append("file", file);
$.ajax({
url: '/uploadAjax',
data: formData,
dataType:'text',
processData: false,
contentType: false,
type: 'POST',
success: function(data){
alert("프로필 이미지가 변경 되었습니다.")
}
})
function checkImageType(fileName){
var pattern = /jpg$|gif$|png$|jpeg$/i;
return fileName.match(pattern);
}
function getOriginalName(fileName){
if(checkImageType(fileName)){
return;
}
var idx = fileName.indexOf("_") + 1 ;
return fileName.substr(idx);
}
function getImageLink(fileName){
if(!checkImageType(fileName)){
return;
}
var front = fileName.substr(0,12);
var end = fileName.substr(14);
return front + end;
}
}
</script>
<c:choose>
<c:when test="${empty userImage }">
<div>
<img id ="profileImg" src = "/displayFile?fileName=/lion.gif" style = "border-radius:0%; padding-top : 10px; height:100px; width:100px;">
</div>
</c:when>
<c:otherwise>
<div>
<img id ="profileImg" src = "/displayFile?fileName=${userImage }" style = "border-radius:0%; padding-top : 10px; height:100px; width:100px;">
</div>
</c:otherwise>
</c:choose>
7 . controller 생성 : ajax 요청을 받는 컨트롤러를 생성한다. 이 컨트롤러에서는 이미지 파일을 변환하여 servlet-context.xml 에서 지정한 저장공간으로 업로드와 동시에 service를 통해 database에 이미지 경로를 저장하게 된다.
@ResponseBody
@RequestMapping(value = "/uploadAjax", method = RequestMethod.POST, produces = "text/plain;charset=UTF-8")
public String uploadAjax(MultipartFile file, String str, HttpSession session,
HttpServletRequest request, Model model) throws Exception {
logger.info("originalName: " + file.getOriginalFilename());
ResponseEntity<String> img_path = new ResponseEntity<>(
UploadFileUtils.uploadFile(uploadPath, file.getOriginalFilename(), file.getBytes()),
HttpStatus.CREATED);
String user_imgPath = (String) img_path.getBody();
logger.info(user_imgPath);
UserVO vo = new UserVO();
vo.setUser_profileImagePath(user_imgPath);
UserVO id = (UserVO) session.getAttribute("login");
System.out.println(id.getUser_id());
vo.setUser_id(id.getUser_id());
logger.info("file name : " + user_imgPath);
return user_imgPath;
}
@ResponseBody
@RequestMapping("/displayFile")
public ResponseEntity<byte[]> displayFile(String fileName) throws Exception {
InputStream in = null;
ResponseEntity<byte[]> entity = null;
logger.info("FILE NAME: " + fileName);
try {
String formatName = fileName.substring(fileName.lastIndexOf(".") + 1);
MediaType mType = MediaUtils.getMediaType(formatName);
HttpHeaders headers = new HttpHeaders();
in = new FileInputStream(uploadPath + fileName);
if (mType != null) {
headers.setContentType(mType);
} else {
fileName = fileName.substring(fileName.indexOf("_") + 1);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.add("Content-Disposition",
"attachment; filename=\"" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1") + "\"");
}
entity = new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
} catch (Exception e) {
e.printStackTrace();
entity = new ResponseEntity<byte[]>(HttpStatus.BAD_REQUEST);
} finally {
in.close();
}
return entity;
}
@ResponseBody
@RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
public ResponseEntity<String> deleteFile(String fileName) {
logger.info("delete file: " + fileName);
String formatName = fileName.substring(fileName.lastIndexOf(".") + 1);
MediaType mType = MediaUtils.getMediaType(formatName);
if (mType != null) {
String front = fileName.substring(0, 12);
String end = fileName.substring(14);
new File(uploadPath + (front + end).replace('/', File.separatorChar)).delete();
}
new File(uploadPath + fileName.replace('/', File.separatorChar)).delete();
return new ResponseEntity<String>("deleted", HttpStatus.OK);
}
8 . Util 파일 작성 java/com/almom/util/UploadFileUtil.java : 업로드에 공통적으로 쓰일 클래스를 생성한다.
package com.almom.util;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.imgscalr.Scalr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;
public class UploadFileUtils {
private static final Logger logger =
LoggerFactory.getLogger(UploadFileUtils.class);
public static String uploadFile(String uploadPath,
String originalName,
byte[] fileData)throws Exception{
UUID uid = UUID.randomUUID();
String savedName = uid.toString() +"_"+originalName;
String savedPath = calcPath(uploadPath);
File target = new File(uploadPath +savedPath,savedName);
FileCopyUtils.copy(fileData, target);
String formatName = originalName.substring(originalName.lastIndexOf(".")+1);
String uploadedFileName = null;
if(MediaUtils.getMediaType(formatName) != null){
uploadedFileName = makeThumbnail(uploadPath, savedPath, savedName);
}else{
uploadedFileName = makeIcon(uploadPath, savedPath, savedName);
}
return uploadedFileName;
}
private static String makeIcon(String uploadPath,
String path,
String fileName)throws Exception{
String iconName =
uploadPath + path + File.separator+ fileName;
return iconName.substring(
uploadPath.length()).replace(File.separatorChar, '/');
}
private static String makeThumbnail(
String uploadPath,
String path,
String fileName)throws Exception{
BufferedImage sourceImg =
ImageIO.read(new File(uploadPath + path, fileName));
BufferedImage destImg =
Scalr.resize(sourceImg,
Scalr.Method.AUTOMATIC,
Scalr.Mode.FIT_TO_HEIGHT,100);
String thumbnailName =
uploadPath + path + File.separator +"s_"+ fileName;
File newFile = new File(thumbnailName);
String formatName =
fileName.substring(fileName.lastIndexOf(".")+1);
ImageIO.write(destImg, formatName.toUpperCase(), newFile);
return thumbnailName.substring(
uploadPath.length()).replace(File.separatorChar, '/');
}
private static String calcPath(String uploadPath){
Calendar cal = Calendar.getInstance();
String yearPath = File.separator+cal.get(Calendar.YEAR);
String monthPath = yearPath +
File.separator +
new DecimalFormat("00").format(cal.get(Calendar.MONTH)+1);
String datePath = monthPath +
File.separator +
new DecimalFormat("00").format(cal.get(Calendar.DATE));
makeDir(uploadPath, yearPath,monthPath,datePath);
logger.info(datePath);
return datePath;
}
private static void makeDir(String uploadPath, String... paths){
if(new File(paths[paths.length-1]).exists()){
return;
}
for (String path : paths) {
File dirPath = new File(uploadPath + path);
if(! dirPath.exists() ){
dirPath.mkdir();
}
}
}
}
9 . main/java/com/almom/util/MediaUtils.java 생성 해당 클래스의 경우 이미지 파일로드시 (JPG, GIF, PNG) 의 데이터형식을 맵에 넣어주는 클래스를 만든다.
package com.almom.util;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.MediaType;
public class MediaUtils {
private static Map<String, MediaType> mediaMap;
static{
mediaMap = new HashMap<String, MediaType>();
mediaMap.put("JPG", MediaType.IMAGE_JPEG);
mediaMap.put("GIF", MediaType.IMAGE_GIF);
mediaMap.put("PNG", MediaType.IMAGE_PNG);
}
public static MediaType getMediaType(String type){
return mediaMap.get(type.toUpperCase());
}
}
10 . Service / DAO / Mapper 생성 : 데이터베이스 관련해서 처리해 줄것은 프로필 이미지 업데이트이다.