这是我制定的解决方案,BalusC 帮助我开发了它。
平台与框架
- 上传v3.2
- JSF 1.2
- jQuery 1.8.3
- WebLogic 10.3.5.0
- Apache 共享文件上传 1.2.2
多部分请求的问题
JSF 1.2 无法处理多部分请求。所以如果<h:form/>
包含enctype="multipart/form-data"
作为属性然后action
命令组件的方法如<h:commandButton/>
不会着火。解决方案是创建一个过滤器,它将与Faces Servlet
并显式处理多部分请求。这是过滤器:
package com.mhis.massupload.filter;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class MultipartFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
chain.doFilter(request, response);
return;
}
HttpServletRequest httpRequest = (HttpServletRequest)request;
boolean isMultipartContent = ServletFileUpload.isMultipartContent(httpRequest);
if (!isMultipartContent) {
chain.doFilter(request, response);
return;
}
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
upload.setSizeMax(-1);
List<FileItem> items = upload.parseRequest(httpRequest);
final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
for (FileItem item : items) {
if (item.isFormField()) {
processFormField(item, parameterMap);
}
}
chain.doFilter(new HttpServletRequestWrapper(httpRequest) {
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
public String[] getParameterValues(String name) {
return (String[])parameterMap.get(name);
}
public String getParameter(String name) {
String[] params = getParameterValues(name);
if (params == null) {
return null;
}
return params[0];
}
public Enumeration<String> getParameterNames() {
return Collections.enumeration(parameterMap.keySet());
}
}, response);
} catch (Exception ex) {
ServletException servletException = new ServletException();
servletException.initCause(ex);
throw servletException;
}
}
private void processFormField(FileItem formField, Map<String, String[]> parameterMap) {
String name = formField.getFieldName();
String value = formField.getString();
String[] values = parameterMap.get(name);
if (values == null) {
parameterMap.put(name, new String[] { value });
} else {
int length = values.length;
String[] newValues = new String[length + 1];
System.arraycopy(values, 0, newValues, 0, length);
newValues[length] = value;
parameterMap.put(name, newValues);
}
}
}
该过滤器在 web.xml 中的配置为:
<filter>
<filter-name>MultipartFilter</filter-name>
<filter-class>com.mhis.massupload.filter.MultipartFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
用于上传的 JavaScript
$('#uploadify').uploadify({
'auto': false,
'buttonText' : 'Browse',
'fileSizeLimit': 0,
'swf': 'uploadify/uploadify.swf',
'uploader': '${pageContext.request.contextPath}/uploadservlet?key=<h:outputText value="#{uploadBean.key}" />',
'onQueueComplete' : function(queueData) {
$('.checkIn').click();
}
});
根据我的要求,我需要在单击表单的提交按钮时上传所有文件,而不是在将文件添加到队列中时上传所有文件。这就是为什么我设置了'auto': false
。 uploadify 文件夹已放入我的项目的 Web-Content 中,因此该插件无法找到uploadify.swf
文件和取消按钮的图像。我必须修改jquery.uploadify.js
的第 99 行,我将其更改为swf: 'uploadify/uploadify.swf'
还有uploadify.css
的第 74 行,我将其更改为:
.uploadify-queue-item .cancel a {
background: url('../uploadify/uploadify-cancel.png') 0 0 no-repeat;
float: right;
height: 16px;
text-indent: -9999px;
width: 16px;
}
The background
被设置为url('../img/uploadify-cancel.png') 0 0 no-repeat;
,但我没有 img 文件夹。
Servlet
为了上传文件,我使用了Servlet
, viz UplaodServlet
;该servlet的配置是:
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.mhis.massupload.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/uploadservlet</url-pattern>
</servlet-mapping>
The uploadservlet
已被用作uploader
的属性uploadify
的配置。我还需要传递一个唯一的密钥作为请求参数Servlet
。代码为Servlet
is:
package com.mhis.massupload.servlet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class UploadServlet extends HttpServlet {
@SuppressWarnings("compatibility:-6472602315203858426")
private static final long serialVersionUID = -3714619333861571457L;
private transient Logger log = Logger.getLogger(getClass().getName());
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
if (!isMultipartContent) {
return;
}
FileItem fileField = null;
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
upload.setSizeMax(-1);
List<FileItem> items = upload.parseRequest(request);
final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
for (FileItem item : items) {
if (!item.isFormField()) {
fileField = item;
}
}
} catch (Exception ex) {
log.severe(ex.getMessage());
}
if (fileField == null) {
return;
}
String key = request.getParameter("key");
List<FileItem> fileFields = (List<FileItem>)getServletContext().getAttribute(key);
if (fileFields == null) {
fileFields = new ArrayList<FileItem>();
getServletContext().setAttribute(key, fileFields);
}
fileFields.add(fileField);
}
}
我无法使用Session
放置有关上传文件的信息,所以我使用了ServletContext
反而。欲了解更多信息,请参阅here https://stackoverflow.com/questions/14275732/passing-value-from-servlet-to-jsf-action-method-in-weblogic.
JSF 页面和上传按钮
由于我的需要是仅在验证后提交表单时上传文件,所以我设置了'auto': false
在uploadify的配置中。但这给我带来了麻烦,我已将这个问题发布在我原来的问题中。为了解决这个问题,我声明了三个input[type=button]
。其中两个是普通的 HTML 按钮,最后一个是<h:commandButton/>
。我已经设置了这个的可见性<h:commandButton/>
设置为 false 并使用了一个虚拟按钮来启动文件上传。上传完成后,我已以编程方式生成了click
of ``. Also I have shown a dummy button which don't have any click event associated with it; this the fail safe if, someone clicks on the Upload button twice during the upload is taking place then the the aforementioned click event fire will work unexpectedly. That why I am showing and hiding the buttons. Here is the completed
.jspx` 页面:
<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
doctype-system="http://www.w3.org/TR/html4/loose.dtd"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<jsp:directive.page contentType="text/html;charset=utf-8"/>
<f:view>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script src="js/jquery-1.8.3.min.js" type="text/javascript"></script>
<script src="uploadify/jquery.uploadify.js" type="text/javascript"></script>
<link rel="stylesheet" media="screen" href="uploadify/uploadify.css" type="text/css"/>
<script type="text/javascript">
$(function() {
$('#uploadify').uploadify({
'auto': false,
'buttonText' : 'Browse',
'fileSizeLimit': 0,
'swf': 'uploadify/uploadify.swf',
'uploader': '${pageContext.request.contextPath}/uploadservlet?key=<h:outputText value="#{uploadBean.key}" />',
'onQueueComplete' : function(queueData) {
$('.checkIn').click();
}
});
$('input[name=actualCheckIn]').on('click', function(event){
event.stopPropagation();
$(this).hide();
$('input[name=fakeCheckIn]').show();
$('#uploadify').uploadify('upload','*');
return false;
});
});
var upload = function() {
}
</script>
<title>Upload</title>
</head>
<body>
<h:form enctype="multipart/form-data">
<input id="uploadify" type="file"/>
<h:commandButton value="Check In" action="#{uploadBean.upload}" styleClass="checkIn" style="display: none"/>
<input type="button" value="Check In" name="actualCheckIn"/>
<input type="button" value="Check In" onclick="return false;" name="fakeCheckIn" style="display: none"/>
</h:form>
</body>
</html>
</f:view>
</jsp:root>
这样,当 uploadify 完成向 servlet 的上传时,就会触发拍卖 JSF。
托管Bean
托管 Bean 的作用域是Session
这是它的代码:
package com.mhis.massupload.bean;
import com.mhis.massupload.dto.DocInfo;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import oracle.stellent.ridc.IdcClientException;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang3.StringUtils;
public class UploadBean implements Serializable {
@SuppressWarnings("compatibility:-930710930183135088")
private static final long serialVersionUID = -491462816764274947L;
private transient Logger log = Logger.getLogger(getClass().getName());
private String key;
private transient Service service;
public UploadBean() throws IdcClientException {
init();
}
private void init() throws IdcClientException {
key = UUID.randomUUID().toString();
}
public String upload() {
List<FileItem> fileFields = (List<FileItem>) FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().remove(key);
List<DocInfo> docInfos = new ArrayList<DocInfo>();
if (fileFields != null) {
for (FileItem fileField : fileFields) {
if(StringUtils.isNotBlank(fileField.getName())) {
try {
System.out.println("Check in: " + fileField.getName());
} catch (Exception e) {
log.log(Level.SEVERE, e.getMessage());
}
}
}
}
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("docInfos", docInfos);
return "report";
}
public void setKey(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}
这很有魅力。
希望它会有所帮助。