Some update:
I have some clue. From the answer: ServletFileUpload#parseRequest(request) returns an empty list
Request instance can only be parsed once I found my struts log when processing request:
2015-11-05 12:23:58,917: DEBUG com.opensymphony.xwork2.interceptor.ParametersInterceptor - Setting params data => [ /Users/zhoum/Documents/tools/apache-tomcat-7.0.55/work/Catalina/localhost/nemo-upload-service/upload_b4a8ce33_67cf_482f_9798_0985ff974c60_00000001.tmp ] dataContentType => [ text/plain ] dataFileName => [ test.txt ] id => [ 1 ]
So I'm afraid struts deals with request before. Is it the problem?
I am working on a Struts2 Restful project. There is a file-uploading api receiving files and parameters. My problem is HttpServletRequest.getPart("filename") always returns null.
The environment is
- multipart/form-data as content-type
- Tomcat7
- Servlet 3.0.1
- JDK 1.6
My code:
package nz.co.niwa.nemo.uploadservice.web.rest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import com.opensymphony.xwork2.ModelDriven;
@MultipartConfig(fileSizeThreshold=1024*1024,
maxFileSize=1024*1024*5, maxRequestSize=1024*1024*5*5)
public class RequestController implements ModelDriven<Map<String, Object>>, ServletRequestAware {
private HttpServletRequest request;
private Map<String, Object> model;
public HttpHeaders test() {
getHttpRequestContent();
return new DefaultHttpHeaders();
}
private void getHttpRequestContent() {
model = new HashMap<String, Object>();
model.put("http method", request.getMethod());
model.put("content length", request.getContentLength());
System.out.println(request.getContentType());
// parts
try {
Part part = request.getPart("data");
model.put("filename", getSubmittedFileName(part));
BufferedReader in = new BufferedReader(new InputStreamReader(part.getInputStream()));
String textString = in.readLine();
// the first line
model.put("content", textString);
} catch (IOException e1) {
e1.printStackTrace();
} catch (ServletException e1) {
e1.printStackTrace();
}
// params
Map<String, Object> params = new HashMap<String, Object>();
for(Enumeration<?> e = request.getParameterNames(); e.hasMoreElements();) {
String paramName = (String)e.nextElement();
params.put(paramName, request.getParameter(paramName));
}
model.put("params", params);
}
@Override
public Map<String, Object> getModel() {
return model;
}
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
private static String getSubmittedFileName(Part part) {
for (String cd : part.getHeader("content-disposition").split(";")) {
if (cd.trim().startsWith("filename")) {
String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
}
}
return null;
}
}
My struts.xml
<?xml version="1.0" encoding="utf-8"?>
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.action.mapAllMatches" value="true"/>
<constant name="struts.convention.default.parent.package" value="rest-default"/>
<!-- Locator is the last word of the package within which controller action class resides. -->
<constant name="struts.convention.package.locators" value="rest"/>
<constant name="struts.rest.defaultExtension" value="json" />
<!-- Set to false if the json content can be returned for any kind of http method -->
<constant name="struts.rest.content.restrictToGET" value="false"/>
My web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- Spring configuration files location -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/*</param-value>
</context-param>
<!-- execution sequence:1. filter 2.servlet -->
<!-- struts filter-->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring listener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Servlet listener that exposes the request to the current thread, through both LocaleContextHolder and RequestContextHolder. -->
<!-- e.g. request can get login users' name and role -->
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<security-constraint>
<web-resource-collection>
<web-resource-name>NEMO UPLOAD RESTFUL</web-resource-name>
<!-- all resources under webapp/ folder are applied this rule -->
<url-pattern>/*</url-pattern>
</web-resource-collection>
<!-- JAAS security section -->
<auth-constraint>
<role-name>admin</role-name>
<role-name>user</role-name>
</auth-constraint>
<!-- SSL -->
<user-data-constraint>
<!-- TODO CONFIDENTIAL -->
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<!-- Basic authentication, your browser will store the user credentials until it's closed. -->
<auth-method>BASIC</auth-method>
<realm-name>NEMO upload service requires authentication.</realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
</web-app>
I found this answer: https://stackoverflow.com/questions/27255132/multipartconfig-annotation-can-be-applied-only-to-servlets
Since mine is struts restful controller doesn't extends Action, so I have no idea how to deal with this situation. And I tried to add an constant in struts.xml:
<constant name="struts.multipart.maxSize" value="30000000" />
But still doesn't work.
One important thing I want to mention is my server(tomcat) has received the file, creating a temp file. When I was debugging in eclipse, I inspected into the HttpServeletRequest instance, it has a multi private valuable containing a valuable called "files". Where I found my uploading file. This is the thing I saw when I use eclipse to inspect HttpServletRequest.multi.files valuable:
{data=[name=test.txt, StoreLocation=/Users/zhoum/Documents/tools/apache-tomcat-7.0.55/work/Catalina/localhost/nemo-upload-service/upload_9dc69a88_ccfa_4ba1_9a8e_ae6912bb6d5c_00000001.tmp, size=23 bytes, isFormField=false, FieldName=data]}