Как использовать 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>

И bean:

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

Ответ 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.

Ответ 2

Ты тоже пользуешься поверхностями? Затем установите диспетчер в положение FORWARD:

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

Ответ 3

Одна точка, которую я заметил с помощью Primefaces 3.4 и Netbeans 7.2:

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

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

Ответ 4

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

Ответ 5

У меня была такая же проблема с файлами 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 значений, но не все из них. Весь раздел контекстного параметра можно удалить, а по умолчанию будет авто

Ответ 6

Ни одно из предложений здесь не помогло мне. Таким образом, мне пришлось отлаживать перфектуры и найти причину проблемы:

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

Затем я добавил раздел в серфинг моих лиц в 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>

Ответ 7

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

См. раздел "Конфликты" Первичные интерфейсы"

Ответ 8

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