Загрузка файла не работает с AJAX в PrimeFaces 4.0/JSF 2.2.x - javax.servlet.ServletException: тип содержимого запроса не является multipart/form-data
Важное замечание : Проблема, обсуждаемая в этой теме, была исправлена в финальной версии PrimeFaces 5.1 (выпуск для сообщества), выпущенной в понедельник, 6 октября 2014 г. (всего несколько минут назад). Я пытался на JSF 2.2.8-02 (или API, Impl).
Таким образом, если вам довелось использовать эту версию (или выше, не нужно упоминать), вам даже не нужно будет читать этот вопрос больше.
У меня запущено веб-приложение
- GlassFish 4.0
- Моджарра 2.2.4
- PrimeFaces 4.0 финал
Все, кроме загрузки файлов с помощью AJAX, работает хорошо. Следующий xhtml-файл отправляет многокомпонентное содержимое с помощью AJAX-запроса, запускаемого кнопкой команды PrimeFaces.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form prependId="true" enctype="multipart/form-data">
<p:fileUpload id="txtCatImage"
value="#{testManagedBean.uploadedFile}"
mode="advanced"
dragDropSupport="true"
fileLimit="1"
sizeLimit="100000"
multiple="false"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
fileUploadListener="#{testManagedBean.fileUploadListener}"/>
<p:message for="txtCatImage" showSummary="false"/>
<p:commandButton id="btnSubmit"
actionListener="#{testManagedBean.insert}"
ajax="true" icon="ui-icon-check" value="Save"/>
</h:form>
</h:body>
</html>
Тестовый боб:
@ManagedBean
@ViewScoped
public final class TestManagedBean implements Serializable {
private static final long serialVersionUID = 1L;
private UploadedFile uploadedFile;
public TestManagedBean(){}
public UploadedFile getUploadedFile() {
return uploadedFile;
}
public void setUploadedFile(UploadedFile uploadedFile) {
this.uploadedFile = uploadedFile;
}
public void fileUploadListener(FileUploadEvent event){
uploadedFile=event.getFile();
}
public void insert(){
if(uploadedFile!=null){
System.out.println(uploadedFile.getFileName());
}
else{
System.out.println("The file object is null.");
}
}
}
Когда файл загружен из файлового браузера, он показывает имя файла в своем слушателе - fileUploadListener()
,
После загрузки файла, когда нажата данная кнопка команды (ajax="true"
), это вызывает следующее исключение.
WARNING: javax.servlet.ServletException: The request content-type is not a multipart/form-data
javax.faces.FacesException: javax.servlet.ServletException: The request content-type is not a multipart/form-data
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decode(NativeFileUploadDecoder.java:44)
at org.primefaces.component.fileupload.FileUploadRenderer.decode(FileUploadRenderer.java:44)
at javax.faces.component.UIComponentBase.decode(UIComponentBase.java:831)
at javax.faces.component.UIInput.decode(UIInput.java:771)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1225)
at javax.faces.component.UIInput.processDecodes(UIInput.java:676)
at javax.faces.component.UIForm.processDecodes(UIForm.java:225)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIViewRoot.processDecodes(UIViewRoot.java:929)
at com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:70)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at filter.NoCacheFilter.doFilter(NoCacheFilter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:722)
Caused by: javax.servlet.ServletException: The request content-type is not a multipart/form-data
at org.apache.catalina.fileupload.Multipart.getPart(Multipart.java:187)
at org.apache.catalina.connector.Request.getPart(Request.java:4535)
at org.apache.catalina.connector.RequestFacade.getPart(RequestFacade.java:1095)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decodeAdvanced(NativeFileUploadDecoder.java:60)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decode(NativeFileUploadDecoder.java:37)
... 48 more
SEVERE: javax.servlet.ServletException: The request content-type is not a multipart/form-data
at org.apache.catalina.fileupload.Multipart.getPart(Multipart.java:187)
at org.apache.catalina.connector.Request.getPart(Request.java:4535)
at org.apache.catalina.connector.RequestFacade.getPart(RequestFacade.java:1095)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decodeAdvanced(NativeFileUploadDecoder.java:60)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decode(NativeFileUploadDecoder.java:37)
at org.primefaces.component.fileupload.FileUploadRenderer.decode(FileUploadRenderer.java:44)
at javax.faces.component.UIComponentBase.decode(UIComponentBase.java:831)
at javax.faces.component.UIInput.decode(UIInput.java:771)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1225)
at javax.faces.component.UIInput.processDecodes(UIInput.java:676)
at javax.faces.component.UIForm.processDecodes(UIForm.java:225)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIViewRoot.processDecodes(UIViewRoot.java:929)
at com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:70)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at filter.NoCacheFilter.doFilter(NoCacheFilter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:722)
Это может работать только тогда, когда атрибут ajax командной кнопки имеет значение false - ajax="false"
,
Я понизил версию Mojarra до 2.1.9 в Tomcat 7.0.35. Он работал с этой версией Mojarra вместе с финальной версией PrimeFaces 4.0 (и PrimeFaces 4.0 RC1 тоже) - файлы, загруженные с помощью AJAX-запроса.
В качестве альтернативы я попробовал следующие версии Mojarra
- 2.2.0
- 2.2.1
- 2.2.2
- 2.2.3
- 2.2.4
в GlassFish 4.0, но ни одному из них не удалось загрузить файлы с помощью запроса AJAX, который крайне необходим, поскольку редактирование строк с использованием <p:rowEditor/>
(вместе с изображениями в каждой строке, например) в PrimeFaces DataTable всегда основывается на AJAX.
Я все равно хочу сохранить GlassFish 4.0. Я также пытался понизить версию Mojarra 2.1.9 в GlassFish 4.0, но он не смог создать пакеты, заканчивающиеся исключением. GlassFish 4.0 не работает с Mojarra ниже 2.2.x.
Итак, что же является причиной этого исключения - PrimeFaces или JSF? Просто запутался. Есть ли обходной путь для загрузки файлов с AJAX-запросами в данной среде?
РЕДАКТИРОВАТЬ:
Отображение фильтра в web.xml
:
<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>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
Проблема остается неизменной вместе с версией Mojarra 2.2.5 (или api, impl), выпущенной 8 января 2014 года.
Еще раз попробовал на версии 2.2.6 Mojarra (или api, impl), выпущенной 4 марта 2014 года. Проблема остается нетронутой.
Все еще не работает на финальной версии PrimeFaces 5.0, выпущенной 5 мая 2014 года.
6 ответов
Я была такая же проблема. Похоже, это больше связано с <p:commandButton>
чем <p:fileUpload>
компонент, так как он работает с <h:commandButton>
(даже с AJAX).
Вы можете попробовать:
<h:commandButton id="btnSubmit" actionListener="#{testManagedBean.insert}" value="Save">
<f:ajax execute="@all" render="@form"/>
</h:commandButton>
Я не могу сказать вам, почему или как это работает, но это исправило проблему для меня. Недостатком, конечно, является то, что вы должны делать стилизацию самостоятельно, по крайней мере, пока ребята из Primefaces не исправят эту проблему.
РЕДАКТИРОВАТЬ:
После поиска в источниках и некоторой отладки, если выяснилось, что на самом деле сделано два запроса (я пытался в <p:wizard/>
). Первый из них multipart/form-data
тот, который на самом деле делает загрузку файла. Он запускает fileUploadEvent в Бине. У волшебников следующая кнопка нажимается другой формой с enctype application/www-urlencoded
представлен Это вызывает исключение. Вывод заключается в том, что в отличие от того, что я написал в комментарии, подавление исключения является допустимым решением. Это может быть сделано даже способом, который не включает изменение Primefaces.jar, что удобно, если парни исправят проблему в будущей версии.
Итак, вот что нужно сделать:
- Создать новый класс
com.yourpackage.fileupload.FileUploadRenderer
Скопируйте и вставьте следующий код в ваш новый класс:
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"); } }
Добавьте следующие строки в нижней части вашего
faces-config.xml
:<render-kit> <renderer> <component-family>org.primefaces.component</component-family> <renderer-type>org.primefaces.component.FileUploadRenderer</renderer-type> <renderer-class>com.yourpackage.fileupload.FileUploadRenderer</renderer-class> </renderer> </render-kit>
Вы готовы к работе!
Что мы наделали? Мы создали наш собственный FileUploadRenderer, который проверяет, действительно ли contentType действительно multipart/form-data
используя метод isMultiPartRequest()
, Только если это возвращает true, остальная часть кода выполняется. В любом другом случае ничего не произойдет, что означает, что исключение не будет выброшено. Если Primefaces исправляет эту проблему, вам просто нужно удалить строки из вашего faces-config.xml
использовать свой класс.
Дайте мне знать, если это работает для вас!
РЕДАКТИРОВАТЬ
Этот код проверяет, является ли данный запрос типа multipart/form-data. Если это не так, выполнение останавливается. Оригинальный код Primefaces все равно продолжится. Как я упоминал выше, если вы загружаете файл внутри компонента Primefaces, на самом деле выполняется два запроса:
- Ajax-FileUpload, использующий
<p:fileUpload/>
(ENCTYPE:multipart/form-data
) - Действие Ajax в
<p:editRow/>
или же<p:wizard/>
(ENCTYPE:application/www-form-urlencoded
)
Первый обрабатывается средством визуализации, а второй вызывает исключение в исходном коде, поскольку средство визуализации пытается обработать что-то, на что оно не способно. Только с изменениями, внесенными в код multipart/form-data
формы обрабатываются средством визуализации, поэтому исключений не возникает. ИМО это явно ошибка в источниках Primefaces. Различия в коде только метод private boolean isMultiPartRequest(FacesContext context)
и его единственное вхождение в коде. Рад, что смог помочь тебе!
Как справедливо указал Кай в своем ответе на текущий вопрос, проблема вызвана NativeFileUploadDecoder
как используется FileUploadRenderer
не проверяет, является ли запрос multipart/form-data
запрос или нет. Это вызовет проблемы, когда компонент присутствует в форме, в которой отправляется "обычный" запрос ajax. CommonsFileUploadDecoder
проверяет это правильно, и именно поэтому он работает правильно в JSF 2.1, который еще не имел собственного анализатора загрузки файлов.
Его решение обойти это с помощью пользовательского рендера - правильное направление, однако этот подход довольно неуклюжий. В этом конкретном случае абсолютно не нужно копировать весь класс, содержащий более 200 строк, просто чтобы добавить еще несколько строк. Вместо этого просто расширите именно этот класс и переопределите точно метод с проверкой if, прежде чем делегировать super следующим образом:
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);
}
}
}
Вот и все <renderer-kit>
вход в faces-config.xml
хоть). Нет смысла продолжать декодирование, если запрос не multipart
запрос. Части файла все равно не будут доступны (и нет смысла возвращаться к javax.servlet.*
API, когда те же функции легко доступны через ExternalContext
).
Хотя это старое и уже отвечено, я хотел поделиться чем-то, на всякий случай, если вы пропустили это: PrimeFaces 4+ теперь имеет параметр контекста, который вы можете использовать (в web.xml), чтобы вручную выбрать, какой загрузчик следует использовать (нативный -сервлет3 или достояние). Вы можете использовать это для принудительной загрузки-загрузки:
<context-param>
<param-name>primefaces.UPLOADER</param-name>
<param-value>commons</param-value>
</context-param>
(Конечно, вам все еще нужен FileUploadFilter, как описано выше и в руководстве). См. Руководство пользователя PrimeFaces для получения дополнительной информации.
@BalusC - Ваше предложение о расширении существующих FileUploadRenderer
очень чисто. Спасибо!
В зависимости от версии JSF, которую вы используете, вы можете увидеть случайное всплывающее окно iFrame. Это ошибка, отмеченная здесь: JAVASERVERFACES-2843
В моей первой попытке это исправить (без необходимости обновления до 2.2.1) я просто спрятал iFrame с помощью CSS.
#JSFFrameId {
visibility:hidden;
}
Это сработало, но по какой-то причине дополнительные представления AJAX не сработали. Затем я вызвал небольшой скрипт для удаления iFrame, и это решило проблему.
<h:commandButton id="btnSubmit" action="#{fileUploadController.upload}" value="Save" >
<f:ajax execute="@all" render="frmMain" onevent="removeIFrame()" />
</h:commandButton>
JavaScript:
function removeIFrame()
{
document.getElementById("JSFFrameId").removeNode();
}
У меня была та же проблема: в моем случае я использовал загрузчик файлов Primefaces в таблице данных, пытался изменить существующее изображение с помощью onRowEdit, что в итоге привело к той же ошибке, упомянутой выше. Затем я изменил банку простых лиц до версии 5.1. Теперь работает нормально.
Пользовательский класс работает для меня:
@Override
public void decode(FacesContext context, UIComponent component) {
if (!context.getExternalContext().getRequestContentType().toLowerCase().startsWith("multipart/")) {
return;
}
FileUpload fileUpload = (FileUpload) component;
if (!fileUpload.isDisabled()) {
PrimeConfiguration cc = RequestContext.getCurrentInstance().getApplicationContext().getConfig();
String uploader = cc.getUploader();
boolean isAtLeastJSF22 = cc.isAtLeastJSF22();
String inputToDecodeId = getSimpleInputDecodeId(fileUpload, context);
if (uploader.equals("auto")) {
if (isAtLeastJSF22)
NativeFileUploadDecoder.decode(context, fileUpload, inputToDecodeId);
else
CommonsFileUploadDecoder.decode(context, fileUpload, inputToDecodeId);
}
else if (uploader.equals("native")) {
NativeFileUploadDecoder.decode(context, fileUpload, inputToDecodeId);
}
else if (uploader.equals("commons")) {
CommonsFileUploadDecoder.decode(context, fileUpload, inputToDecodeId);
}
}
}