- Arawn's Dev Blog
- Outsider's Dev Story
- Toby's Epril
- Benelog
- NHN 개발자 블로그
- SK 플래닛 기술 블로그
- OLC CENTER
- 소프트웨어 경영/공학 블로그
- 모바일 컨버전스
- KOSR - Korea Operating System …
- 넥스트리 블로그
- 리버스코어 ReverseCore
- SLiPP
- 개발자를 위하여... (Nextree 임병인 수석)
- "트위터 부트스트랩: 디자이너도 놀라워할 매끈하고 직관…
- Learning English - The English…
- real-english.com
- 'DataScience/Deep Learning' 카테…
- Deep Learning Summer School, M…
- Deep Learning Courses
민서네집
jQuery Multiple File Upload with Progress bar 본문
jQuery Upload File
https://github.com/hayageek/jquery-upload-file
jQuery Multiple File Upload with Progress bar Tutorial
[원문] http://hayageek.com/jquery-multiple-file-upload/
jQuery Upload File Plugin Demo
http://hayageek.com/docs/jquery-upload-file.php
Jquery Multiple File Upload Demo
http://hayageek.com/examples/jquery/jquery-multiple-file-upload/index.php
화면도 깔끔하고, 한가지 더 좋은 점은 업로드 하는 중간에 cancel 버튼이 있어서 cancel 를 할 수 있다는 것이다.
Spring MVC Framework 를 만들어서 이것을 적용해 보았는데,
Chrome 에서는 잘 되는데, IE 9 에서는 업로드가 완료 안되고, 결과를 download 받으려고 한다.
Multi File Uploader 를 초기화 시킬 때
returnType:"json"
옵션을 줘도 마찬가지다.
Fiddler 에서 HTTP header 정보를 보니 IE 9 에서는
Content-Type: application/json
이러면 문제가 없는것 같은데,
Content-Type: application/json;charset=UTF-8
이렇게 가면 결과를 다운로드 받으려고 한다.
IE 9 의 버그인지...
Sample 페이지를 IE9 에서 실행해 보면 잘 되는데, 업로드한 결과를 Content-Type: text/html 으로 보내주고 있었다.
지금 프로젝트에서는 Spring Framework 3.1.2 를 사용 중인데,
Spring 설정 파일에서
<bean id="jsonView" class="net.sf.json.spring.web.servlet.view.JsonView">
<property name="contentType" value="application/json"/>
</bean>
이렇게 해도
Content-Type: application/json;charset=UTF-8
이렇게 Header 정보가 생긴다.
contentType 의 property 를 삭제해도 디폴트로 이렇게 Content-Type 이 생긴다.
IE 9 에서 Multiple File Upload Example 은 잘 실행된다.
( http://hayageek.com/examples/jquery/jquery-multiple-file-upload/index.php )
Fiddler 로 HTTP Header를 봐 보면
Content-Type: text/html
로 되어 있다.
그래서 jsonView 에서 Content-Type 을 application/json 대신 text/html 로 바꿨다.
구글에서 검색해보니 application/json 으로 하면 문제가 있는 것 같다.
[출처] http://stackoverflow.com/questions/5388893/ie9-json-data-do-you-want-to-open-or-save-this-file
In my case when contentType in response header is "application/json; charset=UTF-8", the IE 9 shows that Prompt. But changed to "text/html" then the prompt does not show, although all other browsers are fine with the "application/json; charset=UTF-8".
[출처] http://stackoverflow.com/questions/267546/correct-content-type-http-header-for-json
application/json is favored but usually web browsers don't know what to do with those headers and screws it up. For stuff like jquery, I have seen text/html recommended. If you are having errors pop up (e.g. download dialog box) then try text/html
Spring 설정 파일에서 다음과 같이 수정했다.
<bean id="jsonView" class="net.sf.json.spring.web.servlet.view.JsonView">
<!-- <property name="contentType" value="application/json;charset=UTF-8"/> -->
<property name="contentType" value="text/html; charset=UTF-8" />
</bean>
HTTP Header 정보가
Content-Type: text/html;charset=UTF-8
이렇게 바뀐다. IE9 에서 jQuery File Upload 도 잘 되고, json 을 사용하는 데도 전혀 문제가 없었다.
대신에 jQuery 에서 $.ajax() 로 call 할 때 dataType 옵션을 안 줬을때 Content-Type 이 application/json 이었을 때는 받아온 문자열을 자동으로 json 객체로 parsing 했지만, Content-Type 을 text/html 로 바꾸게 되면
data = $.parseJSON(data);
이렇게 한번 parsing 을 해줘야 json 객체로 인식한다.
[2015-04-03]
IE9 에서 업로드를 실행하고 나서 그 결과를 다운로드 받으려는 이유는 HTTP Request Header의 Accept 값 때문이었다.
IE9 에서도 jQuery의 $.ajax() 로 호출할 때, (dataType: "json" 옵션을 주고 실행) 정상적으로 json 데이터를 처리한다.
IE9에서 정상적으로 json 객체를 처리한 경우 (Fiddler Application 캡쳐)
아래 이미지는 jQuery Multiple File Upload 할 때, IE9 에서 요청을 보내고 다운로드 받으려고 할 때의 Fiddler 캡쳐한 이미지이다.
아래는 Chrome에서 jQuery Multiple File Upload 라이브러리에서 업로드 할 때의 패킷 캡쳐 이미지이다.
IE9 에서 업로드 하고 나서 결과를 다운로드 받으려 할 때는 Request Header의 Accept 값이 application/json, text/javascript 가 아니라 application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, *.* 으로 이상하게 된 것을 알 수 있다.
https://rawgithub.com/hayageek/jquery-upload-file/master/js/jquery.uploadfile.min.js
http://malsup.github.io/jquery.form.js
위 2개의 라이브러리 중 하나에서 IE9 에서 보낼 때는 Accept 헤더를 잘 못 보내는 버그가 있는 것 같다.
내가 이 버그를 찾아내서 고치기 보다는 그냥 jQuery Multiple File Upload 라이브러리를 이용해서 업로드 할 때만 Response의 Content-Type 을 applicatoin/json 이 아니라 text/html 로 보내서 해결하기로 했다.
Spring Framework의 Controller. (아래와 같이 하면 Response의 Header에서 Content-Type: application/json 으로 된다.)
@RequestMapping(value="/uploadLabExcelFile", method = RequestMethod.POST) @ResponseBody public Map<String , Object> uploadLabExcelFile(@RequestParam Map<String , Object> param, @RequestParam MultipartFile uploadFile) throws IOException { int cnt = labService.uploadLabExcelFile(uploadFile); logger.debug("########### Update Record Count: " + cnt); Map<String , Object> result = new HashMap<String , Object>(); Map<String , Object> data = new HashMap<String , Object>(); data.put("updateCount", cnt); result.put("message", new Message()); result.put("data", data); return result; }
아래와 같이 수정한다. 이렇게 하면 이 Controller 에서만 Response의 Header에서 Content-Type: text/html 으로 되면서 IE9 나 Chrome 에서 jQuery upload library가 다 잘 작동한다.
@RequestMapping(value="/uploadLabExcelFile", method = RequestMethod.POST, produces="text/html") @ResponseBody public String uploadLabExcelFile(@RequestParam Map<String,Object> param, @RequestParam MultipartFile uploadFile) throws IOException { int cnt = labService.uploadLabExcelFile(uploadFile); logger.debug("########### Update Record Count: " + cnt); Map<String , Object> result = new HashMap<String , Object>(); Map<String , Object> data = new HashMap<String , Object>(); data.put("updateCount", cnt); result.put("message", new Message()); result.put("data", data); com.fasterxml.jackson.databind.ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(result); }
[참고] How to Convert Java Object To/From JSON
http://www.mkyong.com/java/how-to-convert-java-object-to-from-json-jackson/
업로드 하기 전에 Check 하는 로직을 추가하려면...
$(document).ready(function() {
// Upload Setting
var settings = {
url: CONTEXT+"/input-data/uploadLabExcelFile",
method: "POST",
returnType:"json",
allowedTypes:"xls,xlsx",
fileName: "uploadFile",
multiple: false,
onSubmit: function(files) {
//files : List of files to be uploaded
//return false; to stop upload
if( $("#selectProjectList").val()=="no-select" ) {
alert("먼저 Project를 선택해 주세요. [onSubmit]");
return false;
}
$("#fileUploaderStatus").html("");
},
onSuccess: function(files,data,xhr)
{
if(data.message.rstCd == "0000") {
$("#fileUploaderStatus").html("<font color='green'>실험 정보 업로드가 성공적으로 처리되었습니다. ( " + data.data.updateCount + " 건)</font>");
} else {
$("#fileUploaderStatus").html("<font color='red'>" + data.rstMsg + "</font>");
alert(data.rstMsg);
if( data.rstCd == "0103" ) {
location.href = "/";
}
}
},
onError: function(files,status,errMsg)
{
$("#fileUploaderStatus").html("<font color='red'>업로드가 실패했습니다.</font>");
}
};
$("#mulitplefileuploader").uploadFile(settings);
setTimeout(function() {
$("input[name=uploadFile]").click(function(){
if( $("#selectProjectList").val()=="no-select" ) {
alert("먼저 Project를 선택해 주세요.");
return false;
}
return true;
});
}, 1000);
});
위에서처럼 setTimeout() 으로 1초를 줘야지 Chrome에서도 제대로 체크가 되었다.
onSubmit: function(files) { ... } 안에 체크 로직을 두면, Upload 버튼을 누를때 바로 체크되는 게 아니라 파일 선택 대화상자가 뜨고 나서 확인 버튼을 누르고 나서야 체크가 된다.
업로드 할 때 부가적인 정보 같이 보내기
[참조] https://github.com/hayageek/jquery-upload-file
Options 항목 중에
dynamicFormData 옵션을 이용하면 된다.
e.g. var settings = { dynamicFormData: function() { var data={"projectId": $('#selectProjectList').val() }; return data; } ... }
부가적인 정보가 바뀔 때마다
$("div.ajax-upload-dragdrop").remove(); 를 하고,
var settings = { formData: "새로운값" , ... };
$("#mulitplefileuploader").uploadFile(settings);
이렇게 했더니, Chrome Browser에서는 잘 수행되지만,
IE9 에서는 아예 upload 가 되지 않았다.
[2015-04-07]
ContentNegotiatingViewResolver 를 이용해서 text/html 로 json 응답 보내기
Spring Framework에서 제공하는 org.springframework.web.servlet.view.BeanNameViewResolver 를 이용해서 text/html 로 json 응답을 보내고 싶었다. 왜냐하면 json 요청일 경우만 따로 처리하는 Exception Handler를 만들고 싶었기 때문이다.
json 요청일 경우는 로그인이 안되어 있는 경우나, 서버에서 에러가 난 경우도 에러 메시지를 json 형태로 줘야지 제대로 처리가 되므로...
그래서 Ajax로 요청을 보낼 때 url 뒤에 .json 을 붙였다. @ExceptionHandler 메서드에서는 request의 URL의 끝에 .json 이 있는지 확인해서 mav.setViewName("jsonView"); 이렇게 처리했다.
package com.examples.project.main.controller; import javax.servlet.http.HttpServletRequest; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; import com.examples.project.common.domain.Message; @ControllerAdvice public class GlobalDefaultExceptionHandler { public static final String DEFAULT_ERROR_VIEW = "error"; @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { // If the exception is annotated with @ResponseStatus rethrow it and let // the framework handle it - like the OrderNotFoundException example // at the start of this post. // AnnotationUtils is a Spring Framework utility class. if( AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) { throw e; } System.out.println(req.getRequestURL()); ModelAndView mav = new ModelAndView(); if( req.getRequestURL().toString().endsWith(".json") ) { mav.setViewName("jsonView"); String errorCode = (String)req.getAttribute("errorCode"); mav.addObject("message", new Message(errorCode, new String[]{e.getMessage()})); } else { // Otherwise setup and send the user to a default error-view. mav.addObject("exception", e); mav.addObject("url", req.getRequestURL()); mav.setViewName("exception/" + DEFAULT_ERROR_VIEW); } return mav; } }
Controller 에서 URL 뒤에 .json이 붙은 응답을 처리하기 위해서는 ContentNegotiatingViewResolver 를 안의 jsonView로 실행되어야 한다.
Controller Method 위에 @ResponseBody 를 붙이면 안되고, @RequestMapping 에서 produces="text/html" 도 넣으면 안된다.
@RequestMapping(value="/uploadLabExcelFile", method = RequestMethod.POST) public String uploadLabExcelFile(@RequestParam Map<String,Object> param, @RequestParam MultipartFile uploadFile, HttpServletRequest request, Model model) throws IOException { String projectId = (String)param.get("projectId"); request.setAttribute("errorCode", "0401"); int cnt = labService.uploadLabExcelFile(uploadFile, projectId); logger.debug("########### Update Record Count: " + cnt); Map<String, Object> data = new HashMap<String, Object>(); data.put("updateCount", cnt); model.addAttribute("message", new Message()); model.addAttribute("data", data); return "jsonView"; }
Spring 설정 파일의 설정은 다음과 같다.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"> <!-- Use spring servlet for all requests, including static resources --> <mvc:default-servlet-handler/> <!-- Use @MVC annotations --> <mvc:annotation-driven /> <!-- User @Controller, @Service... annotations --> <context:component-scan base-package="com.examples.project" /> <!-- **************************************************************** --> <!-- Configration --> <!-- **************************************************************** --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <!-- <value>classpath:config/jdbc.properties</value> --> <value>classpath:config/mail.properties</value> </list> </property> </bean> <util:properties id="config" location="classpath:config/config.properties" /> <util:properties id="mail" location="classpath:config/mail.properties" /> <!-- Application Message Bundle --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename" value="classpath:/message/messages" /> <property name="cacheSeconds" value="3000" /> </bean> <bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"> <!-- <property name="contentType" value="application/json;charset=UTF-8"/> --> <property name="contentType" value="text/html;charset=UTF-8"/> </bean> <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> <property name="contentNegotiationManager"> <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <property name="mediaTypes"> <value> json=text/html;charset=UTF-8 </value> </property> </bean> </property> <property name="viewResolvers"> <list> <bean id="beanNameResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver" /> <bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine" /> <property name="characterEncoding" value="UTF-8" /> </bean> </list> </property> <property name="defaultViews"> <list> <ref bean="jsonView" /> </list> </property> </bean> <!-- Thymeleaf template engine --> <bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML5" /> <property name="characterEncoding" value="UTF-8" /> <!-- Template cache is true by default. Set to false if you want --> <!-- templates to be automatically updated when modified. --> <property name="cacheable" value="false" /> </bean> <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolver" ref="templateResolver" /> <property name="additionalDialects"> <set> <bean class="org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect" /> <bean class="nz.net.ultraq.thymeleaf.LayoutDialect" /> </set> </property> </bean> <!-- **************************************************************** --> <!-- Include Spring Environment --> <!-- **************************************************************** --> <!-- <import resource="spring-exception.xml" /> --> <!-- Java Mail --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="${mail.host}" /> <property name="port" value="${mail.port}" /> <property name="username" value="${mail.senderEmail}" /> <property name="password" value="${mail.senderPassword}" /> <property name="javaMailProperties"> <props> <prop key="mail.transport.protocol">smtp</prop> <prop key="mail.smtp.auth">true</prop> <prop key="mail.smtp.starttls.enable">true</prop> </props> </property> </bean> <!-- **************************************************************** --> <!-- interceptors --> <!-- **************************************************************** --> <mvc:interceptors> <bean class="com.examples.project.common.interceptor.HandlerInterceptor" /> </mvc:interceptors> <!-- For Multipart Upload --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" /> </beans>
[참고]
Spring 3.2로 오면서 바뀐 MVC쪽 설정
http://egloos.zum.com/cj848/v/372874
Spring 3 MVC ContentNegotiatingViewResolver Example
http://www.mkyong.com/spring-mvc/spring-3-mvc-contentnegotiatingviewresolver-example/
3.1.ContentNegotiatingViewResolver
Error Handling for REST with Spring
2. Solution 1 – The Controller level @ExceptionHandler
[2015-08-03]
onSelect 이벤트에서 Ajax로 Data(일련번호)를 가져와서, Hidden Type의 Input Control에 입력해 놓고, dynamicFormData 로 이 값을 서버로 넘겨주면 된다.
[2015-08-05]
onSelect 이벤트에서 어떤 조건에 의해 return false; 를 하게 되면, 동일한 파일을 다시 select 했을 때 onSelect 이벤트가 불리지 않는다. 그래서 onSelect event handler에 있던 return false; 구문을 onSelect event handler로 옮겼다.
멀티 파일 업로드 시에 onSubmit event handler에서 return false;
를 하게 되면 다음 번 업로드 부터는 "afterUploadAll" event 가 불리지 않는다.
http://hayageek.com/docs/jquery-upload-file.php#doc 에 comment 를 달아 놓았다.
'WEB (HTML, CSS)' 카테고리의 다른 글
IE 에서 투명 CSS style (0) | 2013.11.21 |
---|---|
IE8 에서 input type="text" 컨트롤 안에서 글자가 위로 붙는 현상 수정. (0) | 2013.11.21 |
[css] 양쪽 정렬 버튼 (0) | 2013.10.17 |
How to specify the default error page in web.xml? (0) | 2013.10.07 |
input type="file" 에서 찾아보기 버튼 바꾸기 (0) | 2013.09.10 |