¿Cómo usar PrimeFaces p: fileUpload? El método de escucha nunca se invoca o UploadedFile es nulo / arroja un error / no se puede usar

101

Estoy tratando de cargar un archivo usando PrimeFaces, pero el fileUploadListenermétodo no se invoca después de que finaliza la carga.

Aquí está la vista:

<h:form>
    <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
        mode="advanced" 
        update="messages"
        sizeLimit="100000" 
        allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <p:growl id="messages" showDetail="true"/>
</h:form>

Y el frijol:

@ManagedBean
@RequestScoped
public class FileUploadController {

    public void handleFileUpload(FileUploadEvent event) {
        FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

}

He colocado un punto de interrupción en el método, pero nunca se llama. Al usar mode="simple"y ajax="false", se ha invocado, pero quiero que funcione en el modo avanzado. Estoy usando Netbeans y Glassfish 3.1.

Rodrigo Cavalcante
fuente

Respuestas:

224

Cómo configurar y solucionar problemas <p:fileUpload>depende de la versión de PrimeFaces.

Todas las versiones de PrimeFaces

Los siguientes requisitos se aplican a todas las versiones de PrimeFaces:

  1. El enctypeatributo de <h:form>debe establecerse en multipart/form-data. Cuando no está presente, la carga ajax puede funcionar, pero el comportamiento general del navegador no está especificado y depende de la composición del formulario y la marca / versión del navegador web. Solo especifíquelo siempre para estar seguro.

  2. Cuando use mode="advanced"(es decir, carga ajax, este es el valor predeterminado), asegúrese de tener un <h:head>en la plantilla (maestra). Esto asegurará que los archivos JavaScript necesarios se incluyan correctamente. Esto no es necesario para mode="simple"(carga que no sea ajax), pero esto rompería la apariencia y la funcionalidad de todos los demás componentes de PrimeFaces, por lo que no querrá perderse eso de todos modos.

  3. Cuando se usa mode="simple"(es decir, carga que no es ajax), entonces ajax debe estar deshabilitado en cualquier botón / enlace de comando PrimeFaces por ajax="false", y debe usar <p:fileUpload value>con en <p:commandButton action>lugar de <p:fileUpload fileUploadListener>(para PrimeFaces <= 7.x) o <p:fileUpload listener>(para PrimeFaces> = 8.x)

Entonces, si desea cargar (automáticamente) archivos con soporte ajax ( <h:head>¡ tenga en cuenta !):

<h:form enctype="multipart/form-data">
    <p:fileUpload fileUploadListener="#{bean.upload}" auto="true" /> // for PrimeFaces >= 8.x this should be listener instead of fileUploadListener 
</h:form>
public void upload(FileUploadEvent event) {
    UploadedFile uploadedFile = event.getFile();
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}

O si desea cargar un archivo que no sea ajax:

<h:form enctype="multipart/form-data">
    <p:fileUpload mode="simple" value="#{bean.uploadedFile}" />
    <p:commandButton value="Upload" action="#{bean.upload}" ajax="false" />
</h:form>
private UploadedFile uploadedFile; // +getter+setter

public void upload() {
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}

Ten en cuenta que los atributos relacionados con ajax-como auto, allowTypes, update, onstart, oncomplete, etc, son ignorados en mode="simple". Por tanto, no es necesario especificarlos en tal caso.

También tenga en cuenta que debe leer el contenido del archivo inmediatamente dentro de los métodos mencionados anteriormente y no en un método bean diferente invocado por una solicitud HTTP posterior. Esto se debe a que el contenido del archivo cargado tiene un alcance de solicitud y, por lo tanto, no está disponible en una solicitud HTTP posterior / diferente. Cualquier intento de leerlo en una solicitud posterior probablemente terminará java.io.FileNotFoundExceptionen el archivo temporal.


PrimeFaces 8.x

La configuración es idéntica a la información de la versión 5.x a continuación, pero si no se llama a su oyente, compruebe si se llama al attriubute listenery no (como en las versiones anteriores a 8.x)fileUploadListener

PrimeFaces 5.x

Esto no requiere ninguna configuración adicional si está utilizando JSF 2.2 y su faces-config.xmlversión también declarada conforme a JSF 2.2. No necesita el filtro de carga de archivos PrimeFaces en absoluto. En caso de que no le quede claro cómo instalar y configurar correctamente JSF según el servidor de destino utilizado, diríjase a ¿Cómo instalar y configurar correctamente las bibliotecas JSF a través de Maven? y la sección "Instalación de JSF" de nuestra página wiki de JSF .

Sin embargo, si aún no está usando JSF 2.2 y no puede actualizarlo (debería ser fácil cuando ya esté en un contenedor compatible con Servlet 3.0), entonces debe registrar manualmente el filtro de carga de archivos PrimeFaces a continuación en web.xml(analizará el archivo multi solicitud de parte y complete el mapa de parámetros de solicitud regular para que FacesServletpueda continuar funcionando como de costumbre):

<filter>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <servlet-name>facesServlet</servlet-name>
</filter-mapping>

El <servlet-name>valor de facesServletdebe coincidir exactamente con el valor en la <servlet>entrada de javax.faces.webapp.FacesServletla misma web.xml. Entonces, si es por ejemplo Faces Servlet, entonces necesita editarlo en consecuencia para que coincida.


PrimeFaces 4.x

La misma historia que PrimeFaces 5.x se aplica también a 4.x.

Solo existe un problema potencial para obtener el contenido del archivo cargado UploadedFile#getContents(). Esto volverá nullcuando se use la API nativa en lugar de Apache Commons FileUpload. Necesitas usar UploadedFile#getInputStream()en su lugar. Consulte también ¿Cómo insertar una imagen cargada desde p: fileUpload como BLOB en MySQL?

Otro problema potencial que se manifestará con la API nativa es cuando el componente de carga está presente en un formulario en el que se activa una solicitud ajax "regular" diferente que no procesa el componente de carga. Consulte también La carga de archivos no funciona con AJAX en PrimeFaces 4.0 / JSF 2.2.x - javax.servlet.ServletException: el tipo de contenido de la solicitud no es un multipart / form-data .

Ambos problemas también se pueden resolver cambiando a Apache Commons FileUpload. Consulte la sección PrimeFaces 3.x para obtener más detalles.


PrimeFaces 3.x

Esta versión no es compatible con la carga de archivos nativos JSF 2.2 / Servlet 3.0. Debe instalar manualmente Apache Commons FileUpload y registrar explícitamente el filtro de carga de archivos en web.xml.

Necesita las siguientes bibliotecas:

Esos deben estar presentes en la ruta de clases en tiempo de ejecución de la aplicación web. Al usar Maven, asegúrese de que tengan al menos un ámbito de tiempo de ejecución (el ámbito de compilación predeterminado también es bueno). Cuando transporte manualmente los archivos JAR, asegúrese de que terminen en la /WEB-INF/libcarpeta.

El detalle del registro del filtro de carga de archivos se puede encontrar en la sección PrimeFaces 5.x aquí arriba. En caso de que esté usando PrimeFaces 4+ y le gustaría usar explícitamente Apache Commons FileUpload en lugar de la carga de archivos nativos JSF 2.2 / Servlet 3.0, entonces necesita junto a las bibliotecas mencionadas y filtrar también el siguiente parámetro de contexto en web.xml:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
</context-param>

Solución de problemas

En caso de que aún no funcione, aquí hay otras posibles causas no relacionadas con la configuración de PrimeFaces:

  1. Sólo si está utilizando el filtro de carga de archivos PrimeFaces: Hay otra Filteren tu aplicación web que se ejecuta antes de que el filtro de carga de archivos PrimeFaces y ya ha consumido el cuerpo de la petición por ejemplo, llamadas getParameter(), getParameterMap(), getReader(), etcétera. El cuerpo de una solicitud solo se puede analizar una vez. Cuando llama a uno de esos métodos antes de que el filtro de carga de archivos haga su trabajo, el filtro de carga de archivos obtendrá un cuerpo de solicitud vacío.

    Para solucionar esto, debe colocar el <filter-mapping>filtro de carga de archivos antes que el otro filtro web.xml. Si la solicitud no es una multipart/form-datasolicitud, entonces el filtro de carga de archivos continuará como si nada. Si utiliza filtros que se agregan automáticamente porque usan anotaciones (por ejemplo, PrettyFaces), es posible que deba agregar un orden explícito a través de web.xml. Consulte Cómo definir el orden de ejecución del filtro de servlets mediante anotaciones en WAR

  2. Solo si está utilizando el filtro de carga de archivos PrimeFaces: hay otra Filteren su aplicación web que se ejecuta antes del filtro de carga de archivos PrimeFaces y ha realizado una RequestDispatcher#forward()llamada. Por lo general, los filtros de reescritura de URL como PrettyFaces hacen esto. Esto activa el FORWARDdespachador, pero los filtros escuchan de forma predeterminada REQUESTsolo en el despachador.

    Para solucionar esto, deberá colocar el filtro de carga de archivos PrimeFaces antes del filtro de reenvío, o reconfigurar el filtro de carga de archivos PrimeFaces para escuchar también en el FORWARDdespachador:

    <filter-mapping>
        <filter-name>primeFacesFileUploadFilter</filter-name>
        <servlet-name>facesServlet</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
    
  3. Hay un anidado <h:form>. Esto es ilegal en HTML y el comportamiento del navegador no está especificado. La mayoría de las veces, el navegador no enviará los datos esperados al enviarlos. Asegúrese de no anidar <h:form>. Esto es completamente independiente del formulario enctype. Simplemente no anide formas en absoluto.

Si aún tiene problemas, depure el tráfico HTTP. Abra el conjunto de herramientas para desarrolladores del navegador web (presione F12 en Chrome / Firebug23 + / IE9 +) y verifique la sección Red / Red. Si la parte HTTP se ve bien, depure el código JSF. Ponga un punto de ruptura FileUploadRenderer#decode()y avance desde allí.


Guardando archivo subido

Después de que finalmente lo hayas hecho funcionar, tu próxima pregunta probablemente será como "¿Cómo / dónde guardo el archivo cargado?". Bueno, continúe aquí: Cómo guardar el archivo cargado en JSF .

BalusC
fuente
2
Otra causa podría ser que no registró el filtro de carga de PrimeFaces web.xmlsegún la Guía del usuario de PrimeFaces. ¿Lo leíste de todos modos? Sin embargo, eso no explicaría por qué mode="simple"funciona para usted.
BalusC
1
Sí, lo leí y registré el filtro, pero me di cuenta de que me está dando un error al iniciar el servidor "SEVERE: WebModule [/ EventsCalendary] PWC1270: Filtro de inicio de excepción PrimeFaces FileUpload Filter" Me siento tan tonto por no notándolo antes. ¿Algún consejo para solucionar este error?
Rodrigo Cavalcante
2
Quizás el mapeo del filtro sea incorrecto. Tiene que estar mapeado en el <servlet-name>de FacesServletcomo lo ha definido web.xml. La mayoría de los IDE / generadores de código lo predeterminan Faces Servlet, pero puede ser tan bueno facesServleto algo que confirme más las convenciones de nombres.
BalusC
2
Nevermind lo resolvió agregando commons-fileupload a classpath y commons-io, ¿hay alguna parte que diga que estas bibliotecas son necesarias? De todos modos, todo parece estar funcionando en este momento, el método se está llamando como se suponía que debía hacerlo, gracias.
Rodrigo Cavalcante
1
Así que todo surgió con la situación de que tengo la misma configuración en TomEE y JBoss .. forum.primefaces.org/viewtopic.php?f=3&t=43798
Dmitry Alexandrov
30

¿También estás usando caras bonitas? Luego, configure el despachador en FORWARD:

<filter-mapping>
   <filter-name>PrimeFaces FileUpload Filter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>
Reinaldo Gil
fuente
Esto sigue siendo un problema cuando se usa con OCP Rewrite. Te debo una cerveza :)
Babl
7

Un punto que noté con Primefaces 3.4 y Netbeans 7.2:

Elimine los parámetros autocompletados de Netbeans para la función handleFileUpload ie (evento), de lo contrario, el evento podría ser nulo.

<h:form>
    <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload(event)}"
        mode="advanced" 
        update="messages"
        sizeLimit="100000" 
        allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <p:growl id="messages" showDetail="true"/>
</h:form>

fuente
2

Parece que javax.faces.SEPARATOR_CHAR no debe ser igual a _

HazeHorizon
fuente
2
¿Podrías dar más detalles, por favor?
Karl Richter
0

Tuve el mismo problema con Primefaces 5.3 y pasé por todos los puntos descritos por BalusC sin resultado. Seguí su consejo de depurar FileUploadRenderer # decode () y descubrí que mi web.xml no estaba configurado correctamente.

<context-param>
  <param-name>primefaces.UPLOADER</param-name>
  <param-value>auto|native|commons</param-value>
</context-param>

¡El valor de parámetro debe ser 1 de estos 3 valores, pero no todos! Toda la sección de parámetros de contexto se puede eliminar y el valor predeterminado será automático

Eric A
fuente
0

bean.xhtml

    <h:form enctype="multipart/form-data">    
<p:outputLabel value="Choose your file" for="submissionFile" />
                <p:fileUpload id="submissionFile"
                    value="#{bean.file}"
                    fileUploadListener="#{bean.uploadFile}" mode="advanced"
                    auto="true" dragDropSupport="false" update="messages"
                    sizeLimit="100000" fileLimit="1" allowTypes="/(\.|\/)(pdf)$/" />

</h:form>

Bean.java

@ManagedBean

@ViewScoped public class Submission implementa Serializable {

private UploadedFile file;

//Gets
//Sets

public void uploadFasta(FileUploadEvent event) throws FileNotFoundException, IOException, InterruptedException {

    String content = IOUtils.toString(event.getFile().getInputstream(), "UTF-8");

    String filePath = PATH + "resources/submissions/" + nameOfMyFile + ".pdf";

    MyFileWriter.writeFile(filePath, content);

    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO,
            event.getFile().getFileName() + " is uploaded.", null);
    FacesContext.getCurrentInstance().addMessage(null, message);

}

}

web.xml

    <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<filter>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>PrimeFaces FileUpload Filter</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
Waldeyr Mendes da Silva
fuente
¿Puede explicar por qué esta es una respuesta? Es solo código, no explicación o lo que sea
Kukeltje
"# {bean.uploadFile}" vs "# {bean.uploadFasta}", elimine update = "messages" y (solo) eso funciona para mí!
romsky
0

Ninguna de las sugerencias aquí me resultó útil. Así que tuve que depurar primefaces y encontré que la razón del problema era:

java.lang.IllegalStateException: No multipart config for servlet fileUpload

Luego agregué una sección a mi servlet de caras en el web.xml. Entonces eso ha solucionado el problema:

<servlet>
    <servlet-name>main</servlet-name>

        <servlet-class>org.apache.myfaces.webapp.MyFacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <multipart-config>
            <location>/tmp</location>
            <max-file-size>20848820</max-file-size>
            <max-request-size>418018841</max-request-size>
            <file-size-threshold>1048576</file-size-threshold>
        </multipart-config>
    </servlet>
engilyin
fuente
0

Tuve el mismo problema, por el hecho de que tenía toda la configuración que se describe en esta publicación, pero en mi caso fue porque tenía dos importaciones de jquery (una de ellas era la consulta de primefaces) lo que generaba conflictos para subir archivos.

Ver conflicto de Primefaces Jquery

Christian Altamirano Ayala
fuente
Entonces, ¿no recibió un error específico en la consola del desarrollador del navegador?
Kukeltje
@Kukeltje esto es lo que mostró la consola: Uncaught TypeError: Object [object Object] no tiene método 'fileupload'
Christian Altamirano Ayala
0

Para las personas que usan Tomee o Tomcat y no pueden hacerlo funcionar, intente crear context.xml en META-INF y agregue allowCasualMultipartParsing = "true"

<?xml version="1.0" encoding="UTF-8"?>
<Context allowCasualMultipartParsing="true">
  <!-- empty or not depending your project -->
</Context>
Xavier Lambros
fuente
Esta es una solución para una configuración / pedido de filtro incorrecto.
BalusC
Hola @BalusC, ¿puedes darnos más explicaciones? ¿Existe una mejor manera que esta solución?
Xavier Lambros
Vea mi respuesta en esta pregunta.
BalusC
0

Con JBoss 7.2 (Undertow) y PrimeFaces 6.0 org.primefaces.webapp.filter.FileUploadFilter debe eliminarse de web.xml y el cargador de archivos de parámetros de contexto debe configurarse como nativo:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>native</param-value>
</context-param>
Alex D
fuente
¿Debería? ¿Obtiene errores específicos si no los tiene?
Kukeltje
Sí, mi FileUploadEvent no se invoca sin estos cambios.
Alex D
Eso no es un error explícito, es un comportamiento inesperado
Kukeltje