此问题是之前在项目中遇到并解决的,今天将其记录一下。项目中遇到的需求是,要在拦截器中将请求post过来的数据流进行解析,然后手动将解析的各参数其压到struts2的valueStack中,post过来的数据流可能是json串形式(客户端没有标记content-type)或者以multipart/form-data形式发送过来的语音文件(二进制)和json串两种情况。我的实现思路是,判断如果request是multipart的话就是使用commons-file分解出语音数据和json串,否则,就直接使用getInputStream获取出json串。
但是,当传过来的数据是multipart/form-data形式时,发现从request中解析的文件(FileItem)列表为空
获取multipart请求部分代码如下:
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = null;
items = upload.parseRequest(request);
在确定了发送过来的数据没有错误之后,就去网上查找资料,原来,这里的request是被struts2 进行封装的,已经不再是原生的Servlet 请求,所以这个request无法被ServletFileUpload 解析,这就导致了列表为空,解决的方法就是,使用没有被struts2封装过的request,可以在struts.xml中指定文件上传使用的request解析器,如下:
<!-- 使struts2不对request上传请求进行封装 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest"
name="myRequestParser" class="com.dear.vpInterface.utils.RequestParseWrapper"
scope="default" optional="true" />
<constant name="struts.multipart.parser" value="myRequestParser" />
其中com.dear.vpInterface.utils.RequestParseWrapper是自己实现的类:
package com.dear.vpInterface.utils;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
public class RequestParseWrapper extends JakartaMultiPartRequest {
// 这里对parse方法进行了覆盖,使其无法封装request
@Override
public void parse(HttpServletRequest request, String saveDir)
throws IOException {
}
}
这样的话,上述的数据就解决了。
但是,在获取json串的时候,发现使用request.getInputStream()获取的数据竟然也为null。(/(ㄒoㄒ)/~~ ,真是祸不单行,为啥要用struts2),原来在struts2中要去传递过来的报文的content-type进行设置(比如客户端发送json,content-type应为application/json),否则会被struts2拦截,放到request的ParameterMap中,但是由于我只是做服务端的,客户端发送的数据带不带content-type不受我控制,不管客户端带不带content-type,我这边必须都要兼容(/(ㄒoㄒ)/~~),由于传送的请求报文体的不是类似Key=value的形式,而是json,所以这个json应该被放到了ParameterMap的第一个key里面,于是我使用Set<String> set = request.getParameterMap().keySet(),使用遍历这个key的第一个元素,就是json串,事实证明这是正确的。于是问题解决。
以下是具体的自定义的拦截器代码:
package com.dear.vpInterface.interceptor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.struts2.StrutsStatics;
import com.dear.vpInterface.helper.InterException;
import com.dear.vpInterface.utils.JsonUtil;
import com.dear.vpInterface.utils.NameUtil;
import com.dear.vpInterface.utils.PropertyUtil;
import com.dear.vpInterface.utils.HttpError;
import com.dear.vpInterface.utils.StreamUtil;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.util.ValueStack;
public class MessageFormatInterceptor extends AbstractInterceptor{
private static final long serialVersionUID = 1L;
private static final String INTER_EXCEPTION = "interException";
@Override
public String intercept(ActionInvocation invocation) throws Exception{
ActionContext ctx = invocation.getInvocationContext();
HttpServletRequest request= (HttpServletRequest) ctx.get(StrutsStatics.HTTP_REQUEST);
ValueStack stack = ctx.getValueStack();
Map<String,Object> reqMap = null;
try{
reqMap = getMapFromRequestJson(request);
}catch(InterException e){
request.setAttribute("exception", e);
return INTER_EXCEPTION;
}
setParam2Stack(reqMap, stack);
}
return invocation.invoke();
}
//从请求中得到参数Map
public Map<String,Object> getMapFromRequestJson(HttpServletRequest request) throws InterException{
String reqJson = null;
byte[] voiceByte = null;
boolean isVoiceByteExisted = false;
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
try {
if (isMultipart == true) {
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = null;
items = upload.parseRequest(request);
Iterator<FileItem> iterator = items.iterator();
while (iterator.hasNext()) {
FileItem item = iterator.next();
if (item.isFormField()) {
InputStream inputStream = item.getInputStream();
reqJson = StreamUtil.stream2String(inputStream);
} else {
voiceByte = getFile(item);
isVoiceByteExisted = true;
}
}
}else{
reqJson = StreamUtil.stream2String(request.getInputStream()); //客户端指定了Content-Type
if(reqJson == null || "".equals(reqJson)){ //客户端没有指定Content-Type
Set<String> set = request.getParameterMap().keySet();
Iterator<String> it = set.iterator();
if(it.hasNext()){
reqJson = it.next();
}
}
}
} catch (IOException e) {
throw new InterException(HttpError.SMB_HTTP_INPUT_JSON_ERROR);
} catch (FileUploadException e) {
throw new InterException(HttpError.SMB_HTTP_INPUT_JSON_ERROR);
}
Map<String,Object> reqMap = JsonUtil.getMap4Json(reqJson);
if(isVoiceByteExisted){
reqMap.put("voiceByte", voiceByte);
}
return reqMap;
}
//将请求参数压入值栈
public void setParam2Stack(Map<String,Object> reqMap,ValueStack stack){
boolean isVpIdContained = false;
String value = null;
for(String key : reqMap.keySet()){
//将json的下划线命名转换为java的驼峰
stack.setValue(NameUtil.underlineToCamel(key), reqMap.get(key));
}
}
protected static byte[] getFile(FileItem item) throws IOException {
InputStream in = item.getInputStream() ;
ByteArrayOutputStream outStream = new ByteArrayOutputStream() ;
byte[] data = new byte[64*1024] ;
int count = -1 ;
while((count = in.read(data,0,1024)) != -1){
outStream.write(data, 0, count) ;
}
byte[] fileByte = outStream.toByteArray();
in.close();
outStream.close();
return fileByte;
}
}