Как использовать PrimeFaces p:fileUpload? Метод слушателя никогда не вызывается или UploadedFile имеет значение null / выдает ошибку / не используется

Я пытаюсь загрузить файл с помощью PrimeFaces, но fileUploadListener метод не вызывается после завершения загрузки.

Вот мнение:

<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>

И боб:

@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);
    }

}

Я установил точку останова на метод, но он никогда не вызывался. Когда используешь mode="simple" а также ajax="false", он был вызван, но я хочу, чтобы он работал в расширенном режиме. Я использую Netbeans и Glassfish 3.1.

11 ответов

Решение

Как настроить и устранить неполадки <p:fileUpload> зависит от версии PrimeFaces.

Все версии PrimeFaces

Приведенные ниже требования относятся ко всем версиям PrimeFaces:

  1. enctype атрибут <h:form> должен быть установлен в multipart/form-data, Когда это отсутствует, загрузка ajax может просто работать, но общее поведение браузера не определено и зависит от состава формы и производителя / версии веб-браузера. Просто всегда указывайте это, чтобы быть на безопасной стороне.

  2. Когда используешь mode="advanced" (т.е. загрузка по Ajax, это по умолчанию), затем убедитесь, что вы <h:head> в (основном) шаблоне. Это обеспечит правильное включение необходимых файлов JavaScript. Это не требуется для mode="simple" (без загрузки AJAX), но это нарушит внешний вид и функциональность всех других компонентов PrimeFaces, так что вы все равно не захотите это пропустить.

  3. Когда используешь mode="simple" (т. е. загрузка без ajax), тогда 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?

Другая потенциальная проблема с нативным API проявится, когда компонент загрузки присутствует в форме, в которой запускается другой "обычный" запрос ajax, который не обрабатывает компонент загрузки. См. Также Загрузка файла не работает с AJAX в PrimeFaces 4.0/JSF 2.2.x - javax.servlet.ServletException: тип содержимого запроса не является multipart / form-data.

Обе проблемы также могут быть решены путем переключения на Apache Commons FileUpload. Подробности смотрите в разделе PrimeFaces 3.x.


PrimeFaces 3.x

Эта версия не поддерживает загрузку файлов JSF 2.2 / Servlet 3.0. Вам необходимо вручную установить Apache Commons FileUpload и явно зарегистрировать фильтр загрузки файлов в web.xml,

Вам нужны следующие библиотеки:

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

Они должны присутствовать в пути к классам веб-приложения. При использовании 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:

  1. Только если вы используете фильтр загрузки файлов PrimeFaces: есть еще один Filter в вашем веб-приложении, которое запускается до фильтра загрузки файлов PrimeFaces и уже использует тело запроса, например, путем вызова getParameter(), getParameterMap(), getReader() и так далее. Тело запроса может быть проанализировано только один раз. Когда вы вызываете один из этих методов до того, как фильтр загрузки файлов выполнит свою работу, тогда фильтр загрузки файлов получит пустое тело запроса.

    Чтобы это исправить, вам нужно поставить <filter-mapping> фильтра загрузки файлов перед другим фильтром в web.xml, Если запрос не multipart/form-data запрос, то фильтр загрузки файла просто продолжит работу, как будто ничего не произошло. Если вы используете фильтры, которые добавляются автоматически, потому что они используют аннотации (например, PrettyFaces), вам может потребоваться добавить явное упорядочение через web.xml. См. Как определить порядок выполнения фильтра сервлетов с использованием аннотаций в WAR

  2. Только если вы используете фильтр загрузки файлов PrimeFaces: есть еще один Filter в вашем веб-приложении, которое запускается до фильтра загрузки файлов 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>
    
  3. Там вложенный <h:form>, Это недопустимо в HTML, и поведение браузера не определено. Более часто браузер не будет отправлять ожидаемые данные при отправке. Убедитесь, что вы не вкладываете <h:form>, Это полностью независимо от формы enctype, Просто не вкладывайте формы вообще.

Если у вас все еще есть проблемы, отладьте HTTP-трафик. Откройте набор инструментов разработчика веб-браузера (нажмите F12 в Chrome/Firebug23+/IE9+) и проверьте раздел Сеть / Сеть. Если часть HTTP выглядит нормально, отладьте код JSF. Установить точку останова на FileUploadRenderer#decode() и продвигаться оттуда.


Сохранение загруженного файла

После того, как вы, наконец, заставите его работать, ваш следующий вопрос, вероятно, будет таким: "Как / где мне сохранить загруженный файл?". Ну, продолжайте здесь: Как сохранить загруженный файл в JSF.

Вы тоже используете симпатичные лица? Затем установите диспетчер FORWARD:

<filter-mapping>
   <filter-name>PrimeFaces FileUpload Filter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>

Один момент, который я заметил с Primefaces 3.4 и Netbeans 7.2:

Удалите автоматически заполненные параметры Netbeans для функции handleFileUpload, т. Е. (Событие), иначе событие может быть нулевым.

<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>

Похоже, javax.faces.SEPARATOR_CHAR не должно быть равно _

положить p:fileUploadк h:formрешил проблему в моем случае.

Для тех, кто использует Tomee или Tomcat и не может заставить его работать, попробуйте создать context.xml в META-INF и добавить allowCasualMultipartParsing="true"

<?xml version="1.0" encoding="UTF-8"?>
<Context allowCasualMultipartParsing="true">
  <!-- empty or not depending your project -->
</Context>

У меня была та же проблема с простыми лицами 5.3, и я прошел все пункты, описанные BalusC, но безрезультатно. Я последовал его совету по отладке FileUploadRenderer#decode() и обнаружил, что мой файл web.xml установлен неправильно

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

Параметр-значение должно быть 1 из этих 3 значений, но не все из них!! Весь раздел context-param может быть удален, и по умолчанию будет автоматически

Ни одно из предложений здесь не помогло мне. Поэтому я должен был отладить основные лица и нашел причину проблемы:

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

Затем я добавил раздел в мой сервлет Faces в файле web.xml. Итак, это решило проблему:

<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>

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 открытый класс Submission реализует 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>

У меня была та же проблема, из-за того, что у меня была вся конфигурация, описанная в этом посте, но в моем случае это было потому, что у меня было два импорта jquery (один из них был запросом Primefaces), что вызывало конфликты при загрузке файлов.

Смотрите Primefaces Jquery конфликт

С JBoss 7.2(Undertow) и PrimeFaces 6.0 org.primefaces.webapp.filter.FileUploadFilter должен быть удален из web.xml, а загрузчик файла параметров контекста должен быть установлен на собственный:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>native</param-value>
</context-param>
Другие вопросы по тегам