두번째로 AJAX 를 사용해서 파일 업로드 기능을 구현하는 방법입니다


설정은 앞의 스프링 파일 업로드와 거의 유사합니다


AJAX로 업로드를 한 다음 JSON 형식으로 응답을 해주기 위해서

스프링 설정을 좀 추가/수정 해줘야 됩니다


[스프링 설정파일 servlet-context.xml]

<bean id="viewResolver" 

      class="org.springframework.web.servlet.view.InternalResourceViewResolver"

      p:prefix="/WEB-INF/view/" p:suffix=".jsp" p:order="2" />

<bean id="beanNameViewResolver

class="org.springframework.web.servlet.view.BeanNameViewResolver">

<property name="order">

<value>1</value>

</property>

</bean>

<bean name="JSON" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />


스프링에는 @ResponseBody 라는 기능도 있지만 저는 MappingJacksonJsonView 객체를 사용했습니다

Controller에서 리턴해줄 View 를 JSP 대신에 MappingJacksonJsonView 를 사용하는거죠


JSP 페이지로 뿌려준 다음 그 값을 받아도 상관은 없지만.. 

매우 번거로워 지기 때문에 MappingJacksonJsonView를 많이 사용합니다


요놈을 사용하면 매우 편리하게 JSON 형태의 문자열로 변환한 다음 AJAX CallBack 함수로 전달해줄 수 있습니다

JSON 형태의 문자열을 만들기 위해서 JSONObject / JSONArray 등을 따로 사용하지 않아도 됩니다


JSON 이라는 이름을 가진 Bean 객체를 View로 사용해야 하기 때문에 BeanNameViewResolver를 선언해줍니다

처리하는 우선순위는 InternalResourceViewResolver 보다 낮게 지정해야 하구요

그래서 위쪽의 viewResolver는 order를 2로 beanNameViewResolver는 order를 1로 지정했습니다


Service 부분은 앞의 예제를 그대로 사용하시면 됩니다


Controller와 JSP도 살짝 수정해줍니다


[Controller]

@RequestMapping(value="fileUploadAjax.do", method=RequestMethod.POST)

public ModelAndView fileUploadAjax(MultipartHttpServletRequest mRequest) {

ModelAndView mav = new ModelAndView();

if(boardService.fileUpload(mRequest)) {

mav.addObject("result", "SUCCESS");

} else {

mav.addObject("result", "FAIL");

}

mav.setViewName("JSON");

return mav;

}

소스를 보면 앞에서 만든 Controller와 거의 같습니다

View만 JSON 이라고 변경했습니다


[JSP]

<script src="/js/jquery-1.10.2.min.js"></script>

<script src="/js/jquery.form.js"></script>

<script>

$(document).ready(function() {

$("input[type=submit]").bind("click", function() {

$("form").ajaxSubmit({

success : function(data) {

alert(data.result);

},

error : function(error) {

alert("요청 처리 중 오류가 발생하였습니다.");

}

});

return false;

});

});

</script>

jquery.form.js 플러그인을 사용합니다


jquery의 플러그인이므로 임포트 하기 전에 

<script src="/js/jquery-1.10.2.min.js"></script> 이 구문을 반드시 먼저 써줘야 합니다

jquery.form.js 플러그인의 사용방법은 일반 jquery 의 ajax 구문과 거의 같습니다


form 자체에 action 주소가 입력되어 있기 때문에 따로 url을 지정하지 않았지만 

혹시 변경을 하고 싶다면 url : "주소" 이런식으로 추가해주시면 됩니다


가장 아래쪽에 return false; 이 구문을 반드시 추가해주셔야 원래 submit 버튼의 기능을 막아줍니다

추가하지 않으면 ajax로 결과를 받는것이 아니라 다음 페이지로 넘어가서 JSON 문자열을 출력해버립니다


[파일 업로드 페이지]



[파일 업로드 결과]



완성된 프로젝트 파일입니다

FileUploadDownload2.zip







'Spring' 카테고리의 다른 글

스프링 jar 주소  (0) 2014.10.14
스프링을 사용한 파일 업로드 1  (1) 2014.03.04
web.xml 설정  (0) 2013.10.07
component-scan  (0) 2013.09.16
Autowired  (0) 2013.09.16
Posted by 꼬렙
:

스프링을 사용해서 파일 업로드 기능을 구현하는 방법입니다


JSP에서 넘어온 파일 데이터를 Controller에서 직접 처리해도 되겠지만 Service를 거쳐서 처리하도록 하겠습니다


먼저 스프링 설정 파일에 multipartResolver 를 선언해야 합니다

<bean id="multipartResolver

class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

<!-- maximum file size in bytes 50MB -->

<property name="maxUploadSize" value="52428800" />

</bean>

JSP에서 넘어오는 파일 데이터를 받아주는 역할을 합니다


<property>로 사용할수 있는 옵션입니다

maxUploadSize(최대업로드 가능한 바이트크기)

maxInMemorySize(디스크에 임시 파일을 생성하기 전에 메모리에 보관할수있는 최대 바이트 크기)

defaultEncoding(요청을 파싱할 때 사용할 캐릭터 인코딩. 기본값 ISO-8859-1)

특별한 상황이 아니라면 maxUploadSize만 사용해도 무방합니다 (사용하지 않으면 기본값으로 처리)


[Controller]

@Autowired

private BoardService boardService; // 서비스 자동 주입


@RequestMapping(value="fileUpload.do", method=RequestMethod.POST)

public ModelAndView fileUpload(MultipartHttpServletRequest mRequest) {

ModelAndView mav = new ModelAndView();

if(boardService.fileUpload(mRequest)) {

mav.addObject("result", "SUCCESS");

} else {

mav.addObject("result", "FAIL");

}

mav.setViewName("board/fileUpload_result");

return mav;

}

Controller의 메소드의 인자로 MultipartHttpServletRequest 를 사용하였습니다

MultipartHttpServletRequest 이외에도 

Bean 객체, @RequestParam 을 사용하는 방법 등 여러가지가 있습니다


[Service]

public boolean fileUpload(MultipartHttpServletRequest mRequest) {

boolean isSuccess = false;

String uploadPath = "/file/";

File dir = new File(uploadPath);

if (!dir.isDirectory()) {

dir.mkdirs();

}

Iterator<String> iter = mRequest.getFileNames();

while(iter.hasNext()) {

String uploadFileName = iter.next();

MultipartFile mFile = mRequest.getFile(uploadFileName);

String originalFileName = mFile.getOriginalFilename();

String saveFileName = originalFileName;

if(saveFileName != null && !saveFileName.equals("")) {

if(new File(uploadPath + saveFileName).exists()) {

saveFileName = saveFileName + "_" + System.currentTimeMillis();

}

try {

mFile.transferTo(new File(uploadPath + saveFileName));

isSuccess = true;

} catch (IllegalStateException e) {

e.printStackTrace();

isSuccess = false;

} catch (IOException e) {

e.printStackTrace();

isSuccess = false;

}

} // if end

} // while end

return isSuccess;

} // fileUpload end

Controller 에서 받았던 MultipartHttpServletRequest 를 Service로 넘겨줬고

이제 원하는 내용을 꺼내쓰면 됩니다


JSP에서 넘어온 <input> 태그의 name을 확실히 알고 있다면 getFile()확실히 알수 없다면 getFileNames() 를 사용합니다

파일 업로드를 할때 파일을 한개만 올릴수도 있지만.. 여러개를 올리는 경우도 있으므로 getFileNames()를 많이 사용하는 편입니다


Service의 내용은 어떤 name으로 넘어올지 몇개가 넘어올지 모르는 경우라 가정하고 작성했습니다


mRequest.getFileNames()를 사용해서 몇개가 넘어왔든 Iterator 형태로 name을 다 받아주고

그 name으로 mRequest.getFile()을 사용해서 실제 파일 객체를 뽑습니다


그럼 MultipartFile 객체가 나오구요

MultipartFile 객체에서는 업로드한 파일명과 파일사이즈를 뽑아내고 DB에 저장해주면 됩니다

업로드한 파일명은 mFile.getOriginalFilename(), 파일 사이즈는 mFile.getSize()를 쓰면 되구요


이런 과정을 반복해주면 업로드가 끝납니다

물론 처리하는 중에 예상치못한 오류가 발생할 수도 있으니 예외처리는 필수이구요


이렇게 필요한 데이터들을 다 얻어낸 다음에는 업로드된 파일을 실제 물리경로에 저장을 해줘야 됩니다

현재 JSP -> Controller -> Service 이런 과정을 거치긴 했지만 WAS의 임시경로에 저장이 된 상태입니다

만약 여기서 별도 저장하는 작업을 하지 않는다면 임시경로에 잠시 저장되었다가 사라지게 됩니다


그래서 MultipartFile 의 transferTo() 라는 메소드를 사용해서 원하는 위치에 저장해줍니다

InputStream을 얻은 다음에 직접 처리를 해줘도 되지만 성능 좋고 편하니까 transferTo()를 그냥 사용하는 것도 좋습니다


Service 내용 중에서 

if(new File(uploadPath + saveFileName).exists()) {

saveFileName = saveFileName + "_" + System.currentTimeMillis();

}

이런 조건문을 넣었는데요

이런 행위를 하는 이유는 이미 A.txt 라는 파일이 업로드 되어 있는데 또 A.txt를 업로드하면 파일이 덮어져 버리기 때문에

같은 파일명이 존재한다면.. 파일명 맨끝에 현재 시각을 추가하기 위해서 입니다


파일을 실제 저장할때는 현재 시각을 추가했지만 다운로드할때는 원래 업로드 했을때 파일명을 그대로 보여주기 위해서

업로드시 원래 파일명 getOriginalFilename(), 저장된 파일명 saveFileName 이렇게 두가지를 따로 저장해줘야 됩니다


try {

mFile.transferTo(new File(uploadPath + saveFileName));

isSuccess = true;

} catch (IllegalStateException e) {

e.printStackTrace();

isSuccess = false;

} catch (IOException e) {

e.printStackTrace();

isSuccess = false;

}

예외 상황이 발생하지 않는다면 isSuccess가 true, 예외가 발생한다면 false가 저장되어서 Controller로 리턴됩니다


Controller는 그 결과에 따라서 result라는 키로 request에 저장시키고 보여주고자 하는 View(fileUpload_result.jsp)로 넘겨줍니다


View(fileUpload_result.jsp)

<body>

<h1>${result}</h1>

</body>


위 내용들을 실제로 확인해보면..


[파일 업로드 페이지]


[파일 업로드 결과 페이지]

[실제 물리경로 저장]


만약.. 같은 파일들을 또 올린다면..




이렇게 파일명 맨뒤에 현재 시각(유닉스 타임)을 붙여주게 됩니다



최종 프로젝트 파일입니다

FileUploadDownload1.zip









'Spring' 카테고리의 다른 글

스프링 jar 주소  (0) 2014.10.14
스프링을 사용한 파일 업로드 2 (Ajax 파일 업로드)  (1) 2014.03.04
web.xml 설정  (0) 2013.10.07
component-scan  (0) 2013.09.16
Autowired  (0) 2013.09.16
Posted by 꼬렙
:

이제 마지막입니다


[4. socket.io.js를 이용한 웹페이지 작성]


ㅇ jQuery 및 socket.io Import

<script src="/js/jquery-1.10.2.min.js"></script>

<script src="./js/jquery-1.10.2.min.js"></script>

<script src="/js/socket.io.js"></script>

<script src="./js/socket.io.js"></script>


WAS의 server.xml 설정에서 context path 를 / (root) 로 지정하지 않은 경우

JS 에러가 뜨므로 script 경로를 수정했습니다


CDN 을 사용하지 않고 로컬에 저장해놓고 사용했습니다


그래서 WebContent 에 js 폴더를 하나 만들어주고 2개 파일을 넣어줍니다


jquery-1.10.2.min.js


socket.io.js


최종 파일 구성 형태입니다



이제 앞에서 작성했던 main.jsp 와 main2.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 PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>Insert title here</title>

<script src="/js/jquery-1.10.2.min.js"></script>

<script src="/js/socket.io.js"></script>

<script>

$(document).ready(function() {

var socket = io.connect("http://localhost:12345");

socket.on('response', function(msg){

console.log("receive message :: " + msg.msg);

});

$("#sendBtn").bind("click", function() {

var msg = $("input[name=chat]").val();

socket.emit('msg', {msg:msg});

});

});

</script>

</head>

<body>

<h1>Main</h1>

<input type="text" name="chat" />

<input type="button" value="send" id="sendBtn" />

</body>

</html>


두개 파일 내용은 같구요..

각각 다른 주소로 접속을 해도 서로 푸시 기능이 잘 된다는 걸 확인하기 위한 목적이거든요


위 내용을 간략하게 설명하자면..

socket.io 기능을 사용해서 connect를 맺어주고 

서버로부터 특정 키 명으로 오는 메세지를 받을수 있도록  socket.on 을 작성해줍니다


메세지를 보내는 기능도 있어야 하므로 입력창과 버튼을 하나 만들고 

버튼을 클릭하면 socket.emit() 을 사용하여 서버로 전송합니다


그럼 서버에서는 VertxSample 클래스에 작성되어 있는 socket.on 에서 메세지를 받아서 그대로 각 소켓들로 emit을 시켜 줍니다


이런식으로 반복되는 거죠


이제 모든 작업이 끝났습니다


톰캣을 구동시키고 결과를 확인해보겠습니다


확인 방법은 

IE 창 1개 : http://localhost/main.do 접속

크롬 창 2개 : http://localhost/main.do & http://localhost/main2.do (각 다른 주소로 접속)

각 브라우저 개발자도구 - 콘솔 창 오픈




이렇게 3개를 띄워놓은 뒤에 메세지를 입력하고 send 버튼을 누르면 콘솔창에 메세지가 들어와있는걸 확인할 수 있습니다

클라이언트에서는 아무 요청도 하지 않았는데 서버쪽에서 일방적으로 푸시를 하는 거죠 ㅎㅎ


IE 창에서 입력한 결과 입니다



각 창으로 다 메세지가 들어와지구요


크롬에서 해도 마찬가지 입니다



단순히 콘솔 로그로만 출력을 했지만 대화창 모양을 구성해서 출력을 시켜주면 그럴듯한 대화방을 만들수 있습니다


이런 방법들을 응용하면 실시간으로 어떤 사용자가 데이터를 입력했을때 관리자에게 푸시를 해준다던지 등 

여러 재미있는 웹 애플리케이션을 만들수 있을 것 같습니다 ㅎㅎ


최종 완성 프로젝트 파일입니다


Vertx.zip


안된다는 분들 덕분에 추가 설명입니다

당연히 서버의 설정을 맞춰줘야 됩니다


브라우저 실행 예) 캡쳐 이미지에 나와있듯이

현재 localhost 라는 url 다음에 특별히 컨텍스트 패스가 없는 것 처럼 서버의 설정을 맞춰줘야 제대로 실행이 됩니다







Posted by 꼬렙
: