将Flash文件上载与JSF集成

时间:2013-01-10 14:50:33

标签: java jsf file-upload jsf-1.2 swfupload

我已经看到我们可以通过Flash文件上传来上传多个文件。与SWFUploadYUI Uploader一样。是否可以将这些上传组件与JSF集成?

我想要的是一次选择多个文件。 Primefaces文件上传器具有此功能,但在IE7中不起作用,因为IE7没有任何HTML5支持。

我需要创建一个包含各种字段的表单,如下拉菜单,文本输入等,还需要添加文件上传器以选择多个文件。当点击JSF提交按钮时,表格将被验证,并且将在之后继续。

我创建了一个用于上传多个文件的页面,但该页面使用多个输入文件组件作为多个文件。

任何参考对我都非常有帮助。我找到了另一个SO thread,其中发布的解决方案使用JSP。我无法理解如何使用它来满足我的要求。

更新

我创建了以下托管bean:

import com.mhis.massupload.ucm.Service;

import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;

import org.apache.commons.fileupload.FileItem;


public class UploadBean { 
    private Logger log = Logger.getLogger(getClass().getName());
    private Service service;
    private String key;

    public UploadBean() {
        super();
        log.info("JYM");
        init();
    }

    private void init() {
        key = UUID.randomUUID().toString();
    }

    public String upload() {        
        System.out.println("Action Invoked.");
        List<FileItem> fileFields = (List<FileItem>)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key);
        System.out.println(fileFields);

        return "";
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getKey() {
        return key;
    }
}

Servlet是:

import java.io.IOException;

import java.util.ArrayList;
import java.util.List;

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:-3560436533054762606")
    private static final long serialVersionUID = 4122845079663279030L;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("UploadServlet invoked.");        
        List<FileItem> fileFields = new ArrayList<FileItem>();

        try {
            List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
            for (FileItem item : items) {
                if (!item.isFormField()) {
                    fileFields.add(item);
                    System.out.println(item.getName());
                }
            }
        } catch (Exception e) {
            throw new ServletException(e);
        }

        String key = request.getParameter("key");
        request.getSession().setAttribute(key, fileFields);
    }
}

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;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">
            $(document).ready(function() {
                $('#uploadify').uploadify({
                    'swf': 'uploadify/uploadify.swf',
                    'uploader': '${pageContext.request.contextPath}/uploadServlet;jsessionid=${pageContext.session.id}?key=<h:outputText value="#{uploadBean.key}" />'                   
                });                                         
            });    

            var upload = function() {
                $('#uploadify').uploadify('upload','*');
            }
        </script>
        <title>test</title>
      </head>
      <body>
        <h:form enctype="multipart/form-data">
            <input id="uploadify" type="file"/>            
            <h:commandLink action="#{uploadBean.upload}" value="Upload" />
        </h:form>
      </body>
    </html>
  </f:view>
</jsp:root>

我在这里使用Uploadify。

我有两个问题:

  • List<FileItem> fileFields方法的upload有时返回null,有时显示列表。我无法找到原因。我试图通过HttpSession方法从action方法获取FacesContext.getCurrentInstance().getExternalContext().getSession(false),然后在其上调用getAttribute(),这也始终返回null

  • 如果我设置'auto':false,那么文件上传将在调用upload();方法后启动,并将<h:commandLink/>修改为:<h:commandLink action="#{uploadBean.upload}" value="Upload" onclick="upload();"/>然后我会遇到异常,这是:

    org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. EOF after reading only: '2392369' of: '11626364' promised bytes, out of which at least: '0' were already buffered
    at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:367)
    at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:126)
    at com.edfx.massupload.servlet.UploadServlet.doPost(UploadServlet.java:33)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
    at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
    at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
    at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at oracle.security.jps.ee.http.JpsAbsFilter$1.run(JpsAbsFilter.java:119)
    at java.security.AccessController.doPrivileged(Native Method)
    at oracle.security.jps.util.JpsSubject.doAsPrivileged(JpsSubject.java:315)
    at oracle.security.jps.ee.util.JpsPlatformUtil.runJaasMode(JpsPlatformUtil.java:442)
    at oracle.security.jps.ee.http.JpsAbsFilter.runJaasMode(JpsAbsFilter.java:103)
    at oracle.security.jps.ee.http.JpsAbsFilter.doFilter(JpsAbsFilter.java:171)
    at oracle.security.jps.ee.http.JpsFilter.doFilter(JpsFilter.java:71)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at oracle.dms.servlet.DMSServletFilter.doFilter(DMSServletFilter.java:139)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3715)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3681)
    at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
    at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
    at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2277)
    at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2183)
    at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1454)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)
    

在这种情况下,action方法在servlet执行之前执行。

我如何解决这两个问题?

PS

我需要修改uploadify.js以设置swf文件的正确路径并更改取消按钮的css。我已将uploadify的整个目录放在Web-Content中。

2 个答案:

答案 0 :(得分:2)

以下是我所做的解决方案,BalusC帮助我开发了很多。

平台和框架

  • Uploadify v3.2
  • JSF 1.2
  • jQuery 1.8.3
  • WebLogic 10.3.5.0
  • Apache Commons Fileupload 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>

Javactipt for Uploadify

$('#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;
}

background设置为url('../img/uploadify-cancel.png') 0 0 no-repeat;,但我没有img文件夹。

的Servlet

要上传文件,我使用了Servlet,即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>

uploadservlet已被用作uploader配置的uploadify属性。我还需要传递唯一键作为Servlet的请求参数。 Servlet的代码是:

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

JSF页面和上传按钮

由于我的需要是仅在验证后提交表单时上传文件,因此我在uploadify的配置中设置了'auto': false。但这给我带来了麻烦,我在原来的问题中发布了这个问题。为了解决这个问题,我宣布了三个input[type=button]。其中两个是普通的HTML按钮,最后一个是<h:commandButton/>。我已将此<h:commandButton/>的可见性设置为false,并使用了一个虚拟按钮来启动文件上载。上传完成后,我已经以编程方式生成了`click的{​​{1}}。jspx` pag:

. 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

通过这个whay,当uplaodify完成它上传到servlet时,acution JSF被解雇了。

Managed Bean

托管bean的作用域为<?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> ,以下是代码:

Session

这就像魅力一样。

希望它会有所帮助。

答案 1 :(得分:1)

你需要一个servlet。我已经回答了一个Uploadify + Servlet问题,之前应该提供足够的线索:Uploadify plugin doesn't call Java Servlet。 SWFUpload和YUI Uploader的概念并没有那么不同。请注意,您可以将纯HTML放在JSF页面中而不会出现问题。

您的主要问题是启用JSF支持bean和HTTP Servlet以相互交换数据。为此,应使用HTTP会话。首先让JSF bean在初始请求时生成一个唯一的密钥,用作会话属性名称:

key = UUID.randomUUID().toString();

然后,在您告诉Uploadify / SWFUpload / etc使用的上传URL中,您需要包含jsessionid URL片段(以便servlet将使用相同的HTTP会话)和{{1}作为URL查询字符串参数(以便servlet将使用此键存储有关文件上载的相关状态)。假设您需要将其指定为JS对象键:

key

现在,servlet将在同一个HTTP会话中调用(即url: '${pageContext.request.contextPath}/uploadServlet;jsessionid=${pageContext.session.id}?key=<h:outputText value="#{uploadBean.key}" />' request.getSession()中JSF中可用的完全相同,ExternalContext#getSession()的属性完全相同。所以,在getSessionMap()只做:

doPost()

最后,当JSF即将处理表单提交时,只需通过密钥获取所需数据(如果请求作用域为bean,则由String key = request.getParameter("key"); request.getSession().setAttribute(key, someStateAboutTheUpload); <input type="hidden">存储以供后续请求使用):

<t:saveState>

唯一键不是运行所必需的,它甚至可以是静态键,但是基于每个视图生成的键确保当最终用户在多个浏览器选项卡中打开相同的视图时数据不会发生冲突/ windows在同一个会话中。