Spring multipart upload - java

Im trying to write a controller and a form that can handle a multipart file upload and some other data passing.
First i made the basic form like this:
<form:form method="POST" commandName="myForm">
then everything fine, but no multipart handling of course. Then i add the enctype part like this:
<form:form method="POST" commandName="myForm" enctype="multipart/form-data">
Then my whole form is messed up and all the attribuets gives NullPointers. Not even a simple String name attribute working. Also i added:
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
So i really got no idea whats the problem. Any comment would help a lot. Thnaks in advance.

We are using CommonsMultipartResolver in our project. It goes like this. In your applicationContext.xml:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- one of the properties available; the maximum file size in bytes -->
<property name="maxUploadSize" value="1048576000"/>
<property name="defaultEncoding" value="UTF-8" />
</bean>
Then cast yout request to MultipartHttpServletRequest:
public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
if (!(req instanceof MultipartHttpServletRequest)) {
error(resp, "Invalid request (multipart request expected)");
return null;
}
Map<String, MultipartFile> files = ((MultipartHttpServletRequest)req).getFileMap();
... do thomething with the files

Related

spring view resolver for HTML + redirect to apache web server

I have a web server which forwards the dynamic requests with .htm to tomcat (connected using mod_jk) and static resources are served from Apache web server. Consider the following spring controller which process login.htm
#RequestMapping(method = {org.springframework.web.bind.annotation.RequestMethod.POST},
value = {"login.htm"})
public ModelAndView showLoginPage(HttpServletRequest request, HttpServletResponse response) throws Exception {
return new ModelAndView("login");
}
Corresponding viewresolver is as follows:
<!bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".html" />
</bean>
This is searching for a file login.html within the tomcat context. But the static login.html reside on htdocs folder of apache (under same domain).
Can anybody please tell me how can the view be resolved from web server?
It is resolved. In our configuration apache does not allow .html request to tomcat. Earlier I had a misconception that spring would resolve the view via URL and the view would be accessible via web server. Rather spring resolve the view internally from the defined path in viewResolver.
So
<property name="prefix" value="/WEB-INF/html/" />
<property name="suffix" value=".html" />
and html pages in WEB-INF/html would be sufficient to resolve the view.
Spring would never route the view resolving request through web server.

Spring's #RequestBody providing empty string on POST

I have an application with Spring 3.0.5.RELEASE trying to get the full content of a post using #RequestBody. The method is called, but the string passed is always empty. I have checked, by placing breakpoints, that the StringHttpMessageConverter is called, but the inner HttpInputMessage is empty.
I've seen this issue with both Jetty and Tomcat, so I'm discarding it's a problem with the container.
Here is my sample controller:
#Controller
#RequestMapping("/")
public class SubscriptionController {
#RequestMapping(value = "/requestbody", method = RequestMethod.POST)
public ModelAndView mycustomAction(#RequestBody String body) {
// body is always empty
Logger.getLogger(this.getClass()).debug("REQUEST BODY '" + body + "'");
return new ModelAndView("empty");
}
}
My application context is defined as follows:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- Enable auto detection of controllers -->
<context:component-scan base-package="com.big.viajerotelcel.controller" />
<!--
use annotation driven mvc and one single validator with JSR-303
standard
-->
<mvc:annotation-driven />
<!--
Message source for this context, loaded from localized "messages_xx"
files
-->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames" value="classpath:i18n/messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!-- Declare the Interceptor -->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
p:paramName="locale" />
</mvc:interceptors>
<!-- Declare the Resolver -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
<!-- will load Tiles definitions! -->
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/general.xml</value>
</list>
</property>
</bean>
<!-- Tiles view resolver -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>
<!-- Configure the multipart resolver -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--
one of the properties available; the maximum file size in bytes (5MB)
-->
<property name="maxUploadSize" value="5120000" />
</bean>
<!-- Adding these lines has no effect, the StringHttpMessageConverter is called either way -->
<!-- <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>-->
<!-- -->
<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">-->
<!-- <property name="messageConverters">-->
<!-- <list>-->
<!-- <ref bean="stringHttpMessageConverter"/>-->
<!-- </list>-->
<!-- </property>-->
<!-- </bean>-->
</beans>
I'm testing this using curl as follows:
curl -d asd=123 -d qwe=456 http://localhost:8080/requestbody
Any ideas or help is more than welcomed!
Here is a code snippet of ServletServerHttpRequest, which extends HttpInputMessage. I am pretty positive this is the implementation that you are using in your code:
public InputStream getBody() throws IOException {
return this.servletRequest.getInputStream();
}
In other words, the request body is meant to be read as the input stream of the HttpServletRequest object.
The request's input stream is not valid in several situations, but I can't find the correct documentation for it at the moment. For example, if you call request.getParameter() on a post request, tomcat has to read the input stream in order to interpret the parameters, thus afterwards when you read the input stream, it is empty because it has reached the end already.
Perhaps you are invoking getParameter somewhere in an interceptor or perhaps a filter defined in web.xml. Another option is that Spring is doing that for you, for example, if your controller has some other method with complex #RequestMappings (such as reading param values, or header values).
I have two suggestions for you:
Add a servlet filter (before spring gets a chance to act), and wrap the request with your own wrapper (just extend HttpServletRequestWrapper). This way you can put breakpoints or log messages at some methods of the request object and see who's calling them.
Use a pojo object parameter, and setup the bindings. It seems like a much cleaner way to read post data.
How are you POSTing messages to this URL? Are you positive that the HTTP request contains what you think it does? I suggest removing any web browsers from the picture and drop down to something low-level like curl which lets you send any type of HTTP message yourself.
Had a similar problem - the string received by spring controller was always empty. Tinkered with my spring config but with no result. Finally the problem was that the client was actually was not sending anything body!(due to some typo of mine)
If found with a similar error, its worth checking once if the client's payload is actually non-empty.
Another reason that your XML may not be getting marshalled into your JAXB object is related to the namespaces in the XML.
Versions of java after 1.8.101 are more strict about parsing namespaced XML.
See JAXB doesn't unmarshall after updating java from 1.8.0_77 to 1.8.0_121
In my case I was seeing a request body with all nulls and no exception being thrown to indicate that the XML parsing had failed.

Using VelocityView or plain text in Controller response?

I'm trying to return content other than json in my Controller, but I can't seem to get it to work. Ideally, I'd like to return a rendered velocity template as plain text or html.
This is what I have in my controller:
#RequestMapping( value = "time", headers = "Accept=*/*", method = RequestMethod.GET )
public #ResponseBody
Date getCurrentTime( HttpServletRequest request, HttpServletResponse response ) {
response.setContentType( "text/plain" );
return new Date();
}
And this is in my springmvc-servlet.xml (I know this is not right...but I'm a bit lost here):
<context:component-scan base-package="com.paml.alerter.controller" />
<!-- Configures the #Controller programming model -->
<mvc:annotation-driven />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true" />
<property name="prefix" value="" />
<property name="suffix" value=".vm" />
</bean>
<!-- This bean sets up the Velocity environment for us based on a root path
for templates. Optionally, a properties file can be specified for more control
over the Velocity environment, but the defaults are pretty sane for file
based template loading. -->
<bean id="velocityConfig"
class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="resourceLoaderPath" value="/WEB-INF/velocity/" />
</bean>
Does anyone know how to set this up right?
TIA
If your method is annotated with #ResponseBody, then the Spring MVC view layer will be bypassed entirely.
If you're not interested in JSON output, then #ResponseBody is inappropriate - just remove it, and your Velocity views will be used.
If you need to switch between JSON and some other View layer, then you should consider removing #ResponseBody and using ContentNegotiatingViewResolver instead. See the Spring docs for how to set this up.

Uploading file using Spring MVC and CommonsMultipartResolver not working as expected

I am trying to upload files using Spring CommonsMultipartResolver however the controller is not recognised. I get this errror message: "The requested resource (/WebIDE/WEB-INF/views/file/upload.jsp) is not available."
I have added commons-fileupload-1.2.2.jar and commons-io.1.3.2.jar in my library. I have added the following in my application context:
<context:component-scan base-package="org.webide.mvc" />
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- specify maximum file size in bytes -->
<property name="maxUploadSize" value="100000"/>
</bean>
I'm using Pojo as my controller:
#Controller
#RequestMapping (value = "/file")
public class FileController {
#RequestMapping (value = "/upload")
public String uploadFile(#RequestParam("file") CommonsMultipartFile file){
if (!file.isEmpty()){
byte fileBytes[] = file.getBytes();
return "mainView";
}else{
return "errorView";
}
}
My html is quite simple at the moment:
<form method="post" action="file/upload" enctype="multipart/form-data">
<input type="text" name="name"/>
<input type="file" name="file"/>
<input type="submit"/>
</form>
Could you please let me know if I am missing something?
Thanks
The main URL was http://localhost:8084/WebIDE/, then action="/file/upload" should send request to class marked with #Controller. I put a breakpoint and it was not picking up the controller at all.
I had to change my spring configuration to copy everything specified in application context (see above) to dispatcher servlet mvc-config.xml, and also change the way I was declaring them so that the context is the parent of mvc-config.xml
Looks like it did the trick! :)
Thanks again for your help and suggestions.

Spring MVC - Form Mapping

Probably missing something completely obvious here, but here goes. I'm starting out with Spring MVC. I have a form controller to process inbound requests to /share/edit.html. When I hit this url from my browser, I get the following error:
The requested resource (/inbox/share/share/edit) is not available.
Here is my applicationContext-mvc.xml:
<bean id="publicUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
<property name="mappings" >
<value>
/share/edit.html=shareFormController
/share/list.html=shareController
/share/view.html=shareController
/folders.json=foldersController
/studies.json=studiesController
</value>
</property>
</bean>
<bean id="internalPathMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver" />
<bean id="shareFormController" class="com.lifeimage.lila.controller.ShareFormController" />
<bean id="shareController" class="com.lifeimage.lila.controller.ShareController" >
<property name="methodNameResolver" ref="internalPathMethodNameResolver" />
</bean>
and my form Controller:
public class ShareFormController extends SimpleFormController {
public ShareFormController() {
setCommandClass( Share.class );
}
#Override
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
//controller impl...
}
}
You should look at your view resolver. Make sure that it is resolving the logical name in your controller as you think it should. Looks like the name it is resolving it to does not exist currently
I think I've resolved this issue. There were two problems:
1) Implementations of SimpleFormController require a form and success view; which I had not configured here. As this is a server method for an AJAX client, I added a Spring-JSON view as follows:
<?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"
default-lazy-init="false" default-autowire="no"
default-dependency-check="none">
<bean name="jsonView" class="org.springframework.web.servlet.view.json.JsonView">
<property name="jsonErrors">
<list>
<ref bean="statusError" />
<ref bean="modelflagError" />
</list>
</property>
</bean>
<bean name="statusError"
class="org.springframework.web.servlet.view.json.error.HttpStatusError">
<property name="errorCode"><value>311</value></property>
</bean>
<bean name="modelflagError"
class="org.springframework.web.servlet.view.json.error.ModelFlagError">
<property name="name"><value>failure</value></property>
<property name="value"><value>true</value></property>
</bean>
which can be used for all controllers that return JSON.
2) I switched from a SimpleURLHandlerMapping to ControllerClassNameHandlerMapping and relied on Spring naming conventions ( controllerClassName/method.html ), which fixed the routing issue. Might not be a long term solution, but got me through the task.
Did you check your log output? Spring MVC is generally pretty verbose in what it outputs.
Also, the URL you've posted (/inbox/share/share/edit) does not seem to match what you are configuring (/share/edit.html).
#jordan002 when I see all the hoops you had to jump to accomplish your task, I feel obliged to share a very powerful Java MVC framework that requires much less configuration. The framework is called Induction, check out the article Induction vs. Spring MVC, http://www.inductionframework.org/induction-vs-spring-mvc.html

Categories

Resources