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

Resuelto Rodrigo Cavalcante asked hace 12 años • 12 respuestas

Estoy intentando cargar un archivo usando PrimeFaces, pero el fileUploadListenermétodo no se invoca una vez finalizada 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);
    }

}

Coloqué un punto de interrupción en el método, pero nunca se llama. Cuando uso mode="simple"y ajax="false", se invoca, pero quiero que funcione en modo avanzado. Estoy usando Netbeans y Glassfish 3.1.

Rodrigo Cavalcante avatar Jan 16 '12 11:01 Rodrigo Cavalcante
Aceptado

La forma de configurar y solucionar problemas <p:fileUpload>depende de PrimeFaces y la versión JSF.

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 esto 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 de la marca/versión del navegador web. Simplemente especifíquelo siempre para estar seguro.

  2. Cuando utilice mode="advanced"(es decir, carga ajax, este es el valor predeterminado), asegúrese de tener una <h:head>plantilla en la plantilla (maestra). Esto garantizará que los archivos JavaScript necesarios estén incluidos correctamente. Esto no es necesario para mode="simple"(carga que no sea ajax), pero alteraría la apariencia y la funcionalidad de todos los demás componentes de PrimeFaces, por lo que no querrás perdértelo de todos modos.

  3. Cuando se usa mode="simple"(es decir, carga sin ajax), ajax debe estar deshabilitado en cualquier botón/enlace de comando de PrimeFaces mediante ajax="false", y debe usarse <p:fileUpload value>con <p:commandButton action>en lugar de <p:fileUpload listener>.

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

<h:form enctype="multipart/form-data">
    <p:fileUpload listener="#{bean.upload}" auto="true" /> // For PrimeFaces version older than 8.x this should be fileUploadListener instead of listener.
</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 transient UploadedFile uploadedFile; // +getter+setter

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

Tenga en cuenta que los atributos relacionados con ajax, como auto, allowTypes, update, onstart, oncompleteetc., se ignoran en mode="simple". Por lo que no hace falta especificarlos en tal caso.

También tenga en cuenta que la UploadedFilepropiedad se declara transientsolo para crear conciencia de que no es serializable en absoluto. Todo debe colocarse en un bean con ámbito de solicitud en lugar de una vista o incluso uno con ámbito de sesión. Si este es el caso, puede eliminar el transientatributo de forma segura.

También tenga en cuenta que debe leer y guardar inmediatamente el contenido del archivo dentro de los métodos mencionados anteriormente y no en un método de bean diferente invocado por una solicitud HTTP posterior. Esto se debe a que, técnicamente hablando, 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 y solo causará confusión.


PrimeFaces 8.x o posterior

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, verifique si se llama al atributo del método listenery no fileUploadListenercomo en las versiones anteriores a 8.x.


PrimeFaces 5.x

Esto no requiere ninguna configuración adicional si está utilizando al menos JSF 2.2 y faces-config.xmltambién se declara que cumple con al menos la versión JSF 2.2. No necesita el filtro de carga de archivos PrimeFaces en absoluto y tampoco necesita el parámetro de contexto en . En caso de que no le quede claro cómo instalar y configurar JSF correctamente 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 .primefaces.UPLOADERweb.xml

Sin embargo, si aún no está utilizando JSF 2.2 y aún no puede actualizar JSF 2.0/2.1 a 2.2 (aunque debería ser sencillo cuando ya está en un contenedor compatible con Servlet 3.0), entonces deberá registrar manualmente el siguiente filtro de carga de archivos PrimeFaces. in web.xml(analizará la solicitud de varias partes y completará el mapa de parámetros de solicitud normal 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 de la <servlet>entrada javax.faces.webapp.FacesServletdel mismo web.xml. Entonces, si es Faces Servlet, por ejemplo, debe editarlo en consecuencia para que coincida.


PrimeFaces 4.x

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

Sólo existe un problema potencial al obtener el contenido del archivo cargado antes de UploadedFile#getContents(). Esto volverá nullcuando se utilice 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 "normal" 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 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 admite 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:

  • commons-fileupload.jar
  • commons-io.jar

Estos deben estar presentes en la ruta de clase del tiempo de ejecución de la aplicación web. Cuando utilice Maven, asegúrese de que tengan al menos un alcance de tiempo de ejecución (el alcance predeterminado de compilación también es bueno). Cuando transporte manualmente los JAR, asegúrese de que terminen en /WEB-INF/libuna carpeta.

Los detalles de registro del filtro de carga de archivos se pueden encontrar en la sección PrimeFaces 5.x aquí arriba. En caso de que esté utilizando PrimeFaces 4+ y desee utilizar explícitamente Apache Commons FileUpload en lugar de la carga de archivos nativos JSF 2.2/Servlet 3.0, deberá 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. Solo si está utilizando el filtro de carga de archivos PrimeFaces: hay otro Filteren su aplicación web que se ejecuta antes del filtro de carga de archivos PrimeFaces y ya ha consumido el cuerpo de la solicitud, por ejemplo getParameter(), llamando getParameterMap()a , getReader(), etcétera. El cuerpo de una solicitud sólo 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 este problema, deberás colocar el <filter-mapping>filtro de carga de archivos antes que el otro filtro en web.xml. Si la solicitud no es una multipart/form-datasolicitud, entonces el filtro de carga de archivos continuará como si nada hubiera pasado. Si usa filtros que se agregan automáticamente porque usan anotaciones (por ejemplo, PrettyFaces), es posible que necesite agregar un orden explícito a través de web.xml. Ver Cómo definir el orden de ejecución del filtro de servlet usando 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. Normalmente, 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 este problema, 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 que FORWARDtambién escuche en el despachador:

    <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 envía los datos esperados al enviarlos. Asegúrese de no estar anidando <h:form>. Esto es completamente independiente del formulario enctype. Simplemente no anide ningún formulario.

Si todavía tienes problemas, depura el tráfico HTTP. Abra el conjunto de herramientas para desarrolladores del navegador web (presione F12 en Chrome/Firebug23+/IE9+) y consulte la sección Red/Red. Si la parte HTTP se ve bien, depure el código JSF. Pon un punto de interrupción FileUploadRenderer#decode()y avanza desde allí.


Guardando el archivo cargado

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

BalusC avatar Jan 16 '2012 12:01 BalusC

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

<filter-mapping>
   <filter-name>PrimeFaces FileUpload Filter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>
Reinaldo Gil avatar Jan 31 '2012 20:01 Reinaldo Gil

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

Elimine los parámetros autocompletados de Netbeans para la función handleFileUpload, es decir (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>
 avatar Nov 01 '2012 14:11