Saltar al contenido

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

Luego de mucho batallar ya encontramos la contestación de esta escollo que tantos lectores de este espacio han presentado. Si deseas compartir algún detalle no dejes de aportar tu conocimiento.

Solución:

Como Kai señaló con razón en su respuesta a la pregunta actual, el problema es causado por la NativeFileUploadDecoder como lo usa FileUploadRenderer sin comprobar si la solicitud es una multipart/form-data solicitud o no. Esto causará problemas cuando el componente esté presente en un formulario en el que se envía una solicitud ajax “normal”. los CommonsFileUploadDecoder lo comprueba correctamente y es por eso que funciona correctamente en JSF 2.1, que aún no tenía un analizador de carga de archivos nativo.

Su solución de solucionar esto con un renderizador personalizado está en la dirección correcta, sin embargo, el enfoque es bastante torpe. En este caso particular, no hay absolutamente ninguna necesidad de copiar toda la clase que consta de más de 200 líneas solo para agregar algunas líneas más. En su lugar, simplemente extienda exactamente esa clase y anule exactamente el método con una verificación if antes de delegar al super de la siguiente manera:

package com.example;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import org.primefaces.component.fileupload.FileUploadRenderer;

public class MyFileUploadRenderer extends FileUploadRenderer 

    @Override
    public void decode(FacesContext context, UIComponent component) 
        if (context.getExternalContext().getRequestContentType().toLowerCase().startsWith("multipart/")) 
            super.decode(context, component);
        
    


Eso es todo (mantén eso entrada en faces-config.xml aunque). No tiene sentido continuar la decodificación si la solicitud no es una multipart solicitud. Las partes del archivo no estarían disponibles de todos modos (y tampoco tiene sentido volver a javax.servlet.* API cuando la misma funcionalidad está disponible a través de ExternalContext).

Tuve el mismo problema. Parece estar más relacionado con el que la componente, ya que trabaja con un (incluso con ajax).

Tu podrías intentar:


    
 

No puedo decirte por qué o cómo funciona, pero me solucionó el problema. La desventaja, por supuesto, es que tienes que hacer el estilo tú mismo, al menos hasta que los chicos de Primefaces solucionen este problema.

EDITAR:

Después de buscar en las fuentes y depurar un poco, si descubrí que en realidad hay dos solicitudes realizadas (probé ). El primero es el multipart/form-data uno que realmente carga el archivo. Dispara el fileUploadEvent en el Bean. Yo los magos Siguiente se presiona el botón otra forma con enctype application/www-urlencoded se envía. Esto provoca la excepción. La conclusión es que, a diferencia de lo que escribí en el comentario, suprimir la excepción es una solución válida. Esto incluso se puede hacer de una manera que no incluya cambiar Primefaces.jar, lo cual es útil si los chicos solucionan el problema en una versión futura.

Entonces, esto es lo que se debe hacer:

  • Crea una nueva clase com.yourpackage.fileupload.FileUploadRenderer
  • Copie y pegue el siguiente código dentro de su nueva clase:

    package com.yourpackage.fileupload.fileupload;
    
    import java.io.IOException;
    
    import javax.faces.FacesException;
    import javax.faces.component.UIComponent;
    import javax.faces.context.FacesContext;
    import javax.faces.context.ResponseWriter;
    import javax.servlet.http.HttpServletRequest;
    
    import org.primefaces.component.fileupload.CommonsFileUploadDecoder;
    import org.primefaces.component.fileupload.FileUpload;
    import org.primefaces.component.fileupload.NativeFileUploadDecoder;
    import org.primefaces.config.ConfigContainer;
    import org.primefaces.context.RequestContext;
    import org.primefaces.expression.SearchExpressionFacade;
    import org.primefaces.renderkit.CoreRenderer;
    import org.primefaces.util.HTML;        
    import org.primefaces.util.WidgetBuilder;
    
    public class FileUploadRenderer extends CoreRenderer 
    
        @Override
        public void decode(FacesContext context, UIComponent component) 
            FileUpload fileUpload = (FileUpload) component;
    
            if (!fileUpload.isDisabled()) 
                ConfigContainer cc = RequestContext.getCurrentInstance().getApplicationContext().getConfig();
                String uploader = cc.getUploader();
                boolean isAtLeastJSF22 = cc.isAtLeastJSF22();
    
                if (uploader.equals("auto")) 
                    if (isAtLeastJSF22) 
                        if (isMultiPartRequest(context)) 
                            NativeFileUploadDecoder.decode(context, fileUpload);
                        
                     else 
                        CommonsFileUploadDecoder.decode(context, fileUpload);
                    
                 else if (uploader.equals("native")) 
                    if (!isAtLeastJSF22) 
                        throw new FacesException("native uploader requires at least a JSF 2.2 runtime");
                    
    
                    NativeFileUploadDecoder.decode(context, fileUpload);
                 else if (uploader.equals("commons")) 
                    CommonsFileUploadDecoder.decode(context, fileUpload);
                
            
        
    
        @Override
        public void encodeEnd(FacesContext context, UIComponent component) throws IOException 
            FileUpload fileUpload = (FileUpload) component;
    
            encodeMarkup(context, fileUpload);
    
            if (fileUpload.getMode().equals("advanced")) 
                encodeScript(context, fileUpload);
            
        
    
        protected void encodeScript(FacesContext context, FileUpload fileUpload) throws IOException 
            String clientId = fileUpload.getClientId(context);
            String update = fileUpload.getUpdate();
            String process = fileUpload.getProcess();
            WidgetBuilder wb = getWidgetBuilder(context);
            wb.initWithDomReady("FileUpload", fileUpload.resolveWidgetVar(), clientId, "fileupload");
    
            wb.attr("auto", fileUpload.isAuto(), false)
                    .attr("dnd", fileUpload.isDragDropSupport(), true)
                    .attr("update", SearchExpressionFacade.resolveComponentsForClient(context, fileUpload, update), null)
                    .attr("process", SearchExpressionFacade.resolveComponentsForClient(context, fileUpload, process), null)
                    .attr("maxFileSize", fileUpload.getSizeLimit(), Long.MAX_VALUE)
                    .attr("fileLimit", fileUpload.getFileLimit(), Integer.MAX_VALUE)
                    .attr("invalidFileMessage", fileUpload.getInvalidFileMessage(), null)
                    .attr("invalidSizeMessage", fileUpload.getInvalidSizeMessage(), null)
                    .attr("fileLimitMessage", fileUpload.getFileLimitMessage(), null)
                    .attr("messageTemplate", fileUpload.getMessageTemplate(), null)
                    .attr("previewWidth", fileUpload.getPreviewWidth(), 80)
                    .attr("disabled", fileUpload.isDisabled(), false)
                    .callback("onstart", "function()", fileUpload.getOnstart())
                    .callback("onerror", "function()", fileUpload.getOnerror())
                    .callback("oncomplete", "function()", fileUpload.getOncomplete());
    
            if (fileUpload.getAllowTypes() != null) 
                wb.append(",allowTypes:").append(fileUpload.getAllowTypes());
            
    
            wb.finish();
        
    
        protected void encodeMarkup(FacesContext context, FileUpload fileUpload) throws IOException 
            if (fileUpload.getMode().equals("simple")) 
                encodeSimpleMarkup(context, fileUpload);
             else 
                encodeAdvancedMarkup(context, fileUpload);
            
        
    
        protected void encodeAdvancedMarkup(FacesContext context, FileUpload fileUpload) throws IOException 
            ResponseWriter writer = context.getResponseWriter();
            String clientId = fileUpload.getClientId(context);
            String style = fileUpload.getStyle();
            String styleClass = fileUpload.getStyleClass();
            styleClass = styleClass == null ? FileUpload.CONTAINER_CLASS : FileUpload.CONTAINER_CLASS + " " + styleClass;
            boolean disabled = fileUpload.isDisabled();
    
            writer.startElement("div", fileUpload);
            writer.writeAttribute("id", clientId, "id");
            writer.writeAttribute("class", styleClass, styleClass);
            if (style != null) 
                writer.writeAttribute("style", style, "style");
            
    
            //buttonbar
            writer.startElement("div", fileUpload);
            writer.writeAttribute("class", FileUpload.BUTTON_BAR_CLASS, null);
    
            //choose button
            encodeChooseButton(context, fileUpload, disabled);
    
            if (!fileUpload.isAuto()) 
                encodeButton(context, fileUpload.getUploadLabel(), FileUpload.UPLOAD_BUTTON_CLASS, "ui-icon-arrowreturnthick-1-n");
                encodeButton(context, fileUpload.getCancelLabel(), FileUpload.CANCEL_BUTTON_CLASS, "ui-icon-cancel");
            
    
            writer.endElement("div");
    
            //content
            writer.startElement("div", null);
            writer.writeAttribute("class", FileUpload.CONTENT_CLASS, null);
    
            writer.startElement("table", null);
            writer.writeAttribute("class", FileUpload.FILES_CLASS, null);
            writer.startElement("tbody", null);
            writer.endElement("tbody");
            writer.endElement("table");
    
            writer.endElement("div");
    
            writer.endElement("div");
        
    
        protected void encodeSimpleMarkup(FacesContext context, FileUpload fileUpload) throws IOException 
            encodeInputField(context, fileUpload, fileUpload.getClientId(context));
        
    
        protected void encodeChooseButton(FacesContext context, FileUpload fileUpload, boolean disabled) throws IOException 
            ResponseWriter writer = context.getResponseWriter();
            String clientId = fileUpload.getClientId(context);
            String cssClass = HTML.BUTTON_TEXT_ICON_LEFT_BUTTON_CLASS + " " + FileUpload.CHOOSE_BUTTON_CLASS;
            if (disabled) 
                cssClass += " ui-state-disabled";
            
    
            writer.startElement("span", null);
            writer.writeAttribute("class", cssClass, null);
    
            //button icon 
            writer.startElement("span", null);
            writer.writeAttribute("class", HTML.BUTTON_LEFT_ICON_CLASS + " ui-icon-plusthick", null);
            writer.endElement("span");
    
            //text
            writer.startElement("span", null);
            writer.writeAttribute("class", HTML.BUTTON_TEXT_CLASS, null);
            writer.writeText(fileUpload.getLabel(), "value");
            writer.endElement("span");
    
            if (!disabled) 
                encodeInputField(context, fileUpload, clientId + "_input");
            
    
            writer.endElement("span");
        
    
        protected void encodeInputField(FacesContext context, FileUpload fileUpload, String clientId) throws IOException 
            ResponseWriter writer = context.getResponseWriter();
    
            writer.startElement("input", null);
            writer.writeAttribute("type", "file", null);
            writer.writeAttribute("id", clientId, null);
            writer.writeAttribute("name", clientId, null);
    
            if (fileUpload.isMultiple()) 
                writer.writeAttribute("multiple", "multiple", null);
            
            if (fileUpload.getStyle() != null) 
                writer.writeAttribute("style", fileUpload.getStyle(), "style");
            
            if (fileUpload.getStyleClass() != null) 
                writer.writeAttribute("class", fileUpload.getStyleClass(), "styleClass");
            
            if (fileUpload.isDisabled()) 
                writer.writeAttribute("disabled", "disabled", "disabled");
            
    
            writer.endElement("input");
        
    
        protected void encodeButton(FacesContext context, String label, String styleClass, String icon) throws IOException 
            ResponseWriter writer = context.getResponseWriter();
            String cssClass = HTML.BUTTON_TEXT_ICON_LEFT_BUTTON_CLASS + " ui-state-disabled " + styleClass;
    
            writer.startElement("button", null);
            writer.writeAttribute("type", "button", null);
            writer.writeAttribute("class", cssClass, null);
            writer.writeAttribute("disabled", "disabled", null);
    
            //button icon
            String iconClass = HTML.BUTTON_LEFT_ICON_CLASS;
            writer.startElement("span", null);
            writer.writeAttribute("class", iconClass + " " + icon, null);
            writer.endElement("span");
    
            //text
            writer.startElement("span", null);
            writer.writeAttribute("class", HTML.BUTTON_TEXT_CLASS, null);
            writer.writeText(label, "value");
            writer.endElement("span");
            writer.endElement("button");
        
    
        private boolean isMultiPartRequest(FacesContext context) 
            if (context == null) 
                return false;
            
    
            return ((HttpServletRequest) context.getExternalContext().getRequest()).getContentType().startsWith("multipart");
        
    
    
  • Agregue las siguientes líneas en la parte inferior de su faces-config.xml:

    
        
            org.primefaces.component
            org.primefaces.component.FileUploadRenderer
            com.yourpackage.fileupload.FileUploadRenderer
        
    
    
  • Usted está listo para ir!

¿Qué hemos hecho? Creamos nuestro propio FileUploadRenderer que comprueba si el contentType es realmente multipart/form-data usando el método isMultiPartRequest(). Solo si esto vuelve true se ejecuta el resto del código. En cualquier otro caso, no sucederá nada, lo que significa que no se lanzará ninguna excepción. Si Primefaces soluciona este problema, solo necesita eliminar las líneas de su faces-config.xml para usar su clase.

¡Avísame si eso funciona para ti!

EDITAR

Este código verifica si la solicitud dada es de tipo multipart / form-data. Si no es así, se detiene la ejecución. El código original de Primefaces continuaría de todos modos. Como mencioné anteriormente, si carga un archivo dentro de un componente Primefaces, en realidad se realizan dos solicitudes:

  1. El Ajax-FileUpload usando el (enctype: multipart/form-data)
  2. La acción del Ajax en el o (enctype: application/www-form-urlencoded)

El primero es manejado por el renderizador mientras que el segundo causa la excepción en el código original ya que el renderizador intenta manejar algo de lo que no es capaz. Solo con los cambios realizados en el código multipart/form-data Los formularios son manejados por el renderizador por lo que no ocurren excepciones. En mi opinión, es claramente un error en las fuentes de Primefaces. Las diferencias de código son solo el método private boolean isMultiPartRequest(FacesContext context) y su única aparición en el código. ¡Me alegro de poder ayudarte!

Aunque esto es antiguo y ya está respondido, quería compartir algo, en caso de que se lo perdiera: PrimeFaces 4+ ahora tiene un parámetro de contexto, que puede usar (en web.xml) para elegir manualmente qué cargador debe usarse (nativo -servlet3 o commons). Puede usar esto para forzar el cargador común de esta manera:


   primefaces.UPLOADER
   commons

(Por supuesto, todavía necesita FileUploadFilter como se describe arriba y en la guía). Consulte la Guía del usuario de PrimeFaces para obtener más información.

¡Haz clic para puntuar esta entrada!
(Votos: 3 Promedio: 3.7)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *