Ответ 1
Как настроить и устранить неполадки <p:fileUpload>
зависит от версии PrimeFaces.
Все версии PrimeFaces
Приведенные ниже требования относятся ко всем версиям PrimeFaces:
-
Атрибут
enctype
для<h:form>
должен быть установлен вmultipart/form-data
. Когда это отсутствует, загрузка ajax может просто работать, но общее поведение браузера не определено и зависит от состава формы и создания/версии webbrowser. Просто всегда указывайте, что он находится на безопасной стороне. -
При использовании
mode="advanced"
(т.е. загрузка ajax, это значение по умолчанию), затем убедитесь, что у вас есть<h:head>
в шаблоне (мастер). Это обеспечит правильное включение необходимых файлов JavaScript. Это не требуется дляmode="simple"
(non-ajax upload), но это нарушит look'n'feel и функциональность всех других компонентов PrimeFaces, поэтому вы все равно не хотите этого пропустить. -
При использовании
mode="simple"
(т.е. не-ajax upload), ajax должен быть отключен на любых командных кнопках/ссылках PrimeFaces наajax="false"
, и вы должны использовать<p:fileUpload value>
с<p:commandButton action>
вместо<p:fileUpload fileUploadListener>
.
Итак, если вы хотите (авто) загрузить файл с поддержкой ajax (ум <h:head>
!):
<h:form enctype="multipart/form-data">
<p:fileUpload fileUploadListener="#{bean.upload}" auto="true" />
</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!
}
Или если вы хотите загрузить файл не-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!
}
Обратите внимание, что атрибуты, связанные с ajax, такие как auto
, allowTypes
, update
, onstart
, oncomplete
и т.д. игнорируются в mode="simple"
. Поэтому нет необходимости указывать их в этом случае.
Также обратите внимание, что вы должны быстро прочитать содержимое файла внутри вышеупомянутых методов, а не в другом методе bean, вызываемом более поздним HTTP-запросом. Это связано с тем, что содержимое загруженного файла является областью действия запроса и, таким образом, недоступно в более позднем/другом HTTP-запросе. Любая попытка прочитать его в более позднем запросе, скорее всего, закончится с java.io.FileNotFoundException
во временном файле.
PrimeFaces 5.x
Это не требует дополнительной настройки, если вы используете JSF 2.2, а ваш faces-config.xml
также объявлен совместимым с версией JSF 2.2. Вам не нужен фильтр загрузки файлов PrimeFaces. Если вам непонятно, как правильно установить и настроить JSF в зависимости от используемого целевого сервера, перейдите в Как правильно установить и настроить библиотеки JSF через Maven? и "Установка JSF" на нашей странице вики JSF.
Если вы все еще не используете JSF 2.2, и вы не можете его обновить (он должен быть легким, если он уже находится в контейнере, совместимом с Servlet 3.0), вам необходимо вручную зарегистрировать нижеприведенный фильтр загрузки файлов PrimeFaces в web.xml
(он будет анализировать запрос на несколько частей и заполнять карту параметров регулярных запросов, чтобы FacesServlet
мог продолжать работать как обычно):
<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>
Значение <servlet-name>
FacesServlet
должно точно соответствовать значению в записи <servlet>
javax.faces.webapp.FacesServlet
в том же web.xml
. Так, если это, например, Faces Servlet
, тогда вам нужно отредактировать его соответственно для соответствия.
PrimeFaces 4.x
То же самое, что и PrimeFaces 5.x, относится и к 4.x.
Есть только потенциальная проблема в получении загруженного содержимого файла с помощью UploadedFile#getContents()
. Это вернет null
, когда вместо Apache Commons FileUpload будет использоваться собственный API. Вместо этого вам нужно использовать UploadedFile#getInputStream()
. См. Также Как вставить загруженное изображение из p: fileUpload как BLOB в MySQL?
Другая потенциальная проблема с native API будет проявляться в том, что компонент загрузки присутствует в форме, на которой запущен другой "обычный" запрос ajax, который не обрабатывает компонент загрузки. См. Также Загрузка файла не работает с AJAX в PrimeFaces 4.0/JSF 2.2.x - javax.servlet.ServletException: Тип содержимого запроса не является мультиформатным/формальным данным.
Обе проблемы также могут быть решены путем переключения на Apache Commons FileUpload. Подробнее см. В разделе PrimeFaces 3.x.
PrimeFaces 3.x
Эта версия не поддерживает загрузку исходного файла JSF 2.2/Servlet 3.0. Вам необходимо вручную установить Apache Commons FileUpload и явно зарегистрировать фильтр загрузки файлов в web.xml
.
Вам нужны следующие библиотеки:
Те должны присутствовать в пути к классам времени выполнения webapp. При использовании Maven убедитесь, что они, по крайней мере, ограничены средой выполнения (по умолчанию также используется компиляция по умолчанию). При ручном переноске JAR убедитесь, что они попали в папку /WEB-INF/lib
.
Детали регистрации фильтра загрузки файлов можно найти в разделе PrimeFaces 5.x выше. Если вы используете PrimeFaces 4+, и вы хотите явно использовать Apache Commons FileUpload вместо загрузки файлов с исходным файлом JSF 2.2/Servlet 3.0, вам нужно рядом с указанными библиотеками и фильтровать также следующий контекстный параметр в web.xml
<context-param>
<param-name>primefaces.UPLOADER</param-name>
<param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
</context-param>
Устранение неполадок
В случае, если он все еще не работает, вот еще одна возможная причина, не связанная с конфигурацией PrimeFaces:
-
Только если вы используете фильтр загрузки файлов PrimeFaces: там еще
Filter
в вашем webapp, который выполняется перед фильтром загрузки файлов PrimeFaces и уже использовал тело запроса, например. вызываяgetParameter()
,getParameterMap()
,getReader()
и т.д. Тело запроса может анализироваться только один раз. Когда вы вызываете один из этих методов до того, как фильтр загрузки файлов выполнит свое задание, тогда фильтр загрузки файлов получит пустой тело запроса.Чтобы исправить это, вам нужно поместить
<filter-mapping>
фильтра загрузки файлов перед другим фильтром вweb.xml
. Если запрос не является запросомmultipart/form-data
, тогда фильтр загрузки файлов будет продолжаться, как будто ничего не произошло. Если вы используете фильтры, которые автоматически добавляются, потому что они используют аннотации (например, PrettyFaces), вам может потребоваться добавить явное упорядочение через web.xml. См. Как определить порядок выполнения сервлет-фильтра с помощью аннотаций в WAR -
Только если вы используете фильтр загрузки файлов PrimeFaces: там еще
Filter
в вашем webapp, который выполняется перед фильтром загрузки файлов PrimeFaces и выполнилRequestDispatcher#forward()
. Обычно фильтры URL переписывают, например PrettyFaces. Это вызывает диспетчерFORWARD
, но по умолчанию отключает прослушивание только при диспетчереREQUEST
.Чтобы исправить это, вам нужно либо поставить фильтр загрузки файлов PrimeFaces перед фильтром пересылки, либо перенастроить фильтр загрузки файлов PrimeFaces, чтобы прослушивать диспетчер
FORWARD
:<filter-mapping> <filter-name>primeFacesFileUploadFilter</filter-name> <servlet-name>facesServlet</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>
-
Есть вложенный
<h:form>
. Это незаконно в HTML, и поведение браузера не указано. Чаще всего браузер не отправляет ожидаемые данные для отправки. Убедитесь, что вы не вложили<h:form>
. Это абсолютно независимо от формыenctype
. Просто не создавайте формы вообще.
Если у вас все еще есть проблемы, хорошо, отлаживайте HTTP-трафик. Откройте набор инструментов разработчика webbrowser (нажмите F12 в Chrome/Firebug23 +/IE9 +) и проверьте раздел Сеть/Сеть. Если часть HTTP выглядит нормально, отлаживайте код JSF. Поместите точку останова на FileUploadRenderer#decode()
и продвигайтесь оттуда.
Сохранение загруженного файла
После того, как вы, наконец, получите его на работу, ваш следующий вопрос, вероятно, будет выглядеть следующим образом: "Как/где я могу сохранить загруженный файл?". Итак, продолжайте здесь: Как сохранить загруженный файл в JSF.