Jak korzystać z PrimeFaces p: fileUpload? Metoda Listener nigdy nie jest wywoływana lub UploadedFile ma wartość null / zgłasza błąd / nie nadaje się do użytku

101

Próbuję przesłać plik przy użyciu PrimeFaces, ale fileUploadListenermetoda nie jest wywoływana po zakończeniu przesyłania.

Oto widok:

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

I fasola:

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

}

Umieściłem punkt przerwania w metodzie, ale nigdy nie został wywołany. Podczas używania mode="simple"i ajax="false"jest wywoływany, ale chcę, aby działał w trybie zaawansowanym. Używam Netbeans i Glassfish 3.1.

Rodrigo Cavalcante
źródło

Odpowiedzi:

226

Sposób konfiguracji i rozwiązywania problemów <p:fileUpload>zależy od wersji PrimeFaces.

Wszystkie wersje PrimeFaces

Poniższe wymagania dotyczą wszystkich wersji PrimeFaces:

  1. enctypeAtrybutem <h:form>musi być ustawiony na multipart/form-data. Gdy tego nie ma, ładowanie AJAX może po prostu działać, ale ogólne zachowanie przeglądarki jest nieokreślone i zależy od składu formularza oraz marki / wersji przeglądarki internetowej. Po prostu zawsze określaj to jako bezpieczną stronę.

  2. Używając mode="advanced"(tj. Wysyłania AJAX, jest to ustawienie domyślne), upewnij się, że masz a <h:head>w (głównym) szablonie. Zapewni to prawidłowe dołączenie niezbędnych plików JavaScript. Nie jest to wymagane w przypadku mode="simple"(wysyłania bez AJAX), ale mogłoby to zepsuć wygląd i funkcjonalność wszystkich innych komponentów PrimeFaces, więc i tak nie chcesz tego przegapić.

  3. W przypadku korzystania z mode="simple"(tj. Przesyłania bez AJAX), Ajax musi być wyłączony na każdym przycisku / łączu poleceń PrimeFaces przez ajax="false"i należy używać <p:fileUpload value>z <p:commandButton action>zamiast <p:fileUpload fileUploadListener>(dla PrimeFaces <= 7.x) lub <p:fileUpload listener>(dla PrimeFaces> = 8.x)

Tak więc, jeśli chcesz (automatycznie) przesyłać pliki z obsługą Ajax (uwaga <h:head>!):

<h:form enctype="multipart/form-data">
    <p:fileUpload fileUploadListener="#{bean.upload}" auto="true" /> // for PrimeFaces >= 8.x this should be listener instead of fileUploadListener 
</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!
}

Lub jeśli chcesz przesłać plik bez 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!
}

Należy pamiętać, że atrybuty związane z AJAX, takie jak auto, allowTypes, update, onstart, oncomplete, etc są ignorowane w mode="simple". W takim przypadku nie trzeba więc ich określać.

Zauważ również, że powinieneś czytać zawartość pliku bezpośrednio wewnątrz wyżej wymienionych metod, a nie w innej metodzie bean wywołanej późniejszym żądaniem HTTP. Dzieje się tak, ponieważ zawartość przesłanego pliku jest objęta zakresem żądania, a zatem jest niedostępna w późniejszym / innym żądaniu HTTP. Każda próba odczytania go w późniejszym żądaniu najprawdopodobniej zakończy się java.io.FileNotFoundExceptionw pliku tymczasowym.


PrimeFaces 8.x

Konfiguracja jest identyczna z informacjami o wersji 5.x poniżej, ale jeśli twój słuchacz nie jest wywoływany, sprawdź, czy attriubute jest wywoływany, listenera nie (tak jak w przypadku wersji pre 8.x)fileUploadListener

PrimeFaces 5.x

Nie wymaga to żadnej dodatkowej konfiguracji, jeśli używasz JSF 2.2, a Twoja faces-config.xmlwersja jest również zgodna z JSF 2.2. W ogóle nie potrzebujesz filtra przesyłania plików PrimeFaces. Jeśli nie jest dla Ciebie jasne, jak poprawnie zainstalować i skonfigurować JSF w zależności od używanego serwera docelowego, przejdź do Jak poprawnie zainstalować i skonfigurować biblioteki JSF przez Maven? oraz sekcja „Instalowanie JSF” na naszej stronie wiki JSF .

Jeśli jednak nie używasz jeszcze JSF 2.2 i nie możesz go zaktualizować (powinno to być łatwe, gdy jest już w kontenerze kompatybilnym z Servletem 3.0), musisz ręcznie zarejestrować poniższy filtr przesyłania plików PrimeFaces w web.xml(przeanalizuje on multi żądanie części i wypełnij mapę parametrów zwykłego żądania, aby FacesServletkontynuować normalną pracę):

<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>Wartość facesServletmusi odpowiadać dokładnie wartość w <servlet>wpisu javax.faces.webapp.FacesServletw tym samym web.xml. Więc jeśli jest to np. Faces Servlet, Musisz go odpowiednio edytować, aby pasował.


PrimeFaces 4.x

Ta sama historia, co w przypadku PrimeFaces 5.x, dotyczy również wersji 4.x.

Istnieje tylko potencjalny problem z pobraniem zawartości przesłanego pliku przez UploadedFile#getContents(). nullZwróci to, gdy zamiast Apache Commons FileUpload zostanie użyte natywne API. Zamiast tego musisz użyć UploadedFile#getInputStream(). Zobacz też Jak wstawić wgrany obraz z p: fileUpload jako BLOB w MySQL?

Innym potencjalnym problemem związanym z natywnym interfejsem API jest pojawienie się komponentu wysyłającego w postaci, w której jest uruchamiane inne „zwykłe” żądanie ajax, które nie przetwarza komponentu do przesyłania. Zobacz także Przesyłanie plików nie działa z AJAX w PrimeFaces 4.0 / JSF 2.2.x - javax.servlet.ServletException: Żądanie typu content-type nie jest multipart / form-data .

Oba problemy można również rozwiązać, przełączając się na Apache Commons FileUpload. Szczegółowe informacje można znaleźć w sekcji PrimeFaces 3.x.


PrimeFaces 3.x

Ta wersja nie obsługuje natywnego przesyłania plików JSF 2.2 / Servlet 3.0. Musisz ręcznie zainstalować Apache Commons FileUpload i jawnie zarejestrować filtr przesyłania plików w web.xml.

Potrzebujesz następujących bibliotek:

Muszą one znajdować się w ścieżce klas środowiska wykonawczego aplikacji internetowej. Korzystając z Mavena, upewnij się, że mają one przynajmniej zakres czasu wykonywania (domyślny zakres kompilacji jest również dobry). Podczas ręcznego przenoszenia plików JAR upewnij się, że trafiają do /WEB-INF/libfolderu.

Szczegóły rejestracji filtra przesyłania plików można znaleźć w sekcji PrimeFaces 5.x powyżej. Jeśli używasz PrimeFaces 4+ i chcesz jawnie użyć Apache Commons FileUpload zamiast natywnego przesyłania plików JSF 2.2 / Servlet 3.0, musisz obok wspomnianych bibliotek i przefiltrować również poniższy parametr kontekstu w web.xml:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
</context-param>

Rozwiązywanie problemów

Jeśli nadal nie działa, oto inne możliwe przyczyny niezwiązane z konfiguracją PrimeFaces:

  1. Tylko jeśli używasz filtra Prześlij plik PrimeFaces: Jest inna Filterw webapp, która biegnie przed filtrem Prześlij plik PrimeFaces i już zużyte ciało żądanie np powołania getParameter(), getParameterMap(), getReader(), etcetera. Treść żądania można przeanalizować tylko raz. Gdy wywołasz jedną z tych metod, zanim filtr przesyłania plików wykona swoje zadanie, filtr przesyłania plików otrzyma pustą treść żądania.

    Aby to naprawić, musisz umieścić <filter-mapping>filtr przesyłania plików przed drugim filtrem web.xml. Jeśli żądanie nie jest multipart/form-datażądaniem, filtr przesyłania plików będzie kontynuowany tak, jakby nic się nie stało. Jeśli używasz filtrów, które są dodawane automagicznie, ponieważ używają adnotacji (np. PrettyFaces), może być konieczne dodanie jawnej kolejności za pośrednictwem web.xml. Zobacz Jak zdefiniować kolejność wykonywania filtrów serwletów za pomocą adnotacji w WAR

  2. Tylko jeśli używasz filtru przesyłania plików PrimeFaces: Filterw Twojej aplikacji internetowej jest inna, która działa przed filtrem przesyłania plików PrimeFaces i wykonała RequestDispatcher#forward()połączenie. Zwykle robią to filtry przepisujące adresy URL, takie jak PrettyFaces . Spowoduje to wyzwolenie programu FORWARDrozsyłającego, ale filtry nasłuchują domyślnie REQUESTtylko w programie wysyłającym.

    Aby to naprawić, musisz albo umieścić filtr przesyłania plików PrimeFaces przed filtrem przekierowania, albo ponownie skonfigurować filtr przesyłania plików PrimeFaces, aby również nasłuchiwał w FORWARDdyspozytorze:

    <filter-mapping>
        <filter-name>primeFacesFileUploadFilter</filter-name>
        <servlet-name>facesServlet</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
    
  3. Jest zagnieżdżony <h:form>. Jest to nielegalne w HTML, a zachowanie przeglądarki jest nieokreślone. Najczęściej przeglądarka nie wysyła oczekiwanych danych po przesłaniu. Upewnij się, że nie zagnieżdżasz się <h:form>. Jest to całkowicie niezależne od formy enctype. Po prostu w ogóle nie zagnieżdżaj form.

Jeśli nadal masz problemy, cóż, zdebuguj ruch HTTP. Otwórz zestaw narzędzi programistycznych przeglądarki internetowej (naciśnij klawisz F12 w przeglądarce Chrome / Firebug23 + / IE9 +) i sprawdź sekcję Sieć / Sieć. Jeśli część HTTP wygląda dobrze, zdebuguj kod JSF. Umieść punkt przerwania FileUploadRenderer#decode()i przejdź dalej.


Zapisywanie przesłanego pliku

Gdy w końcu zaczniesz działać, Twoje następne pytanie będzie prawdopodobnie brzmiało: „Jak / gdzie zapisać przesłany plik?”. Cóż, kontynuuj tutaj: Jak zapisać przesłany plik w JSF .

BalusC
źródło
2
Inną przyczyną może być to, że nie zarejestrowałeś filtra przesyłania PrimeFaces web.xmlzgodnie z Przewodnikiem użytkownika PrimeFaces. Czytałeś to mimo wszystko? To jednak nie wyjaśnia, dlaczego mode="simple"działa dla Ciebie.
BalusC
1
Tak, przeczytałem i zarejestrowałem filtr, ale właśnie zauważyłem, że powoduje on błąd podczas uruchamiania serwera "POWAŻNY: WebModule [/ EventsCalendary] PWC1270: Wyjątek filtru startowego PrimeFaces FileUpload Filter" Czuję się głupio, że nie zauważając to wcześniej. Jakieś wskazówki dotyczące rozwiązania tego błędu?
Rodrigo Cavalcante
2
Być może mapowanie filtra jest nieprawidłowe. Musi być odwzorowany na <servlet-name>z, FacesServletjak zdefiniowałeś w web.xml. Większość IDE / generatorów kodu domyślnie Faces Servletprzyjmuje wartość, ale może być równie dobra facesServletlub bardziej zgodna z konwencjami nazewnictwa.
BalusC
2
Nevermind rozwiązał to, dodając commons-fileupload do classpath i commons-io, czy jest gdzieś powiedzenie, że te biblioteki są potrzebne? W każdym razie wszystko wydaje się teraz działać, metoda nazywa się tak, jak powinna, dzięki.
Rodrigo Cavalcante
1
Więc wszystko wyszło z tego, że mam taką samą konfigurację w TomEE i JBoss .. forum.primefaces.org/viewtopic.php?f=3&t=43798
Dmitry Alexandrov
30

Ty też używasz prettyfaces? Następnie ustaw dyspozytora na FORWARD:

<filter-mapping>
   <filter-name>PrimeFaces FileUpload Filter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>
Reinaldo Gil
źródło
Jest to nadal problem, gdy jest używany z OCP Rewrite. Jestem ci winien piwo :)
Babl
7

Jeden punkt, który zauważyłem w Primefaces 3.4 i Netbeans 7.2:

Usuń automatycznie wypełniane parametry Netbeans dla funkcji handleFileUpload ie (zdarzenie), w przeciwnym razie zdarzenie mogłoby mieć wartość null.

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

źródło
2

Wygląda na to, że javax.faces.SEPARATOR_CHAR nie może być równe _

HazeHorizon
źródło
2
Czy mógłbyś to rozwinąć, proszę ?!
Karl Richter,
0

Miałem ten sam problem z primefaces 5.3 i przeszedłem przez wszystkie punkty opisane przez BalusC bez rezultatu. Postępowałem zgodnie z jego radą dotyczącą debugowania FileUploadRenderer # decode () i odkryłem, że mój web.xml jest nieprawidłowo ustawiony

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

Wartość parametru musi wynosić 1 z tych 3 wartości, ale nie wszystkie !! Całą sekcję context-param można usunąć, a domyślną wartością będzie auto

eric A.
źródło
0

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 implementuje 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>
Waldeyr Mendes da Silva
źródło
Czy możesz wyjaśnić, dlaczego to jest odpowiedź? To tylko kod, a nie wyjaśnienie czy cokolwiek innego
Kukeltje
"# {bean.uploadFile}" vs "# {bean.uploadFasta}", usuń update = "messages" i to (tylko) będzie działać dla mnie!
romsky
0

Żadna z poniższych sugestii nie była dla mnie pomocna. Musiałem więc debugować primefaces i znalazłem przyczynę problemu:

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

Następnie dodałem sekcję do mojego serwletu twarzy w pliku web.xml. Więc to rozwiązało problem:

<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>
engilyin
źródło
0

Miałem ten sam problem ze względu na to, że miałem całą konfigurację opisaną w tym poście, ale w moim przypadku było to spowodowane tym, że miałem dwa importy jquery (jeden z nich był zapytaniem primefaces), co powodowało konflikty w przesyłaniu plików.

Zobacz konflikt Primefaces Jquery

Christian Altamirano Ayala
źródło
Czy nie pojawił się wtedy konkretny błąd w konsoli programisty przeglądarki?
Kukeltje
@Kukeltje tak właśnie pokazała konsola: Uncaught TypeError: Object [object Object] nie ma metody „fileupload”
Christian Altamirano Ayala,
0

W przypadku osób używających Tomee lub Tomcat i nie można ich uruchomić, spróbuj utworzyć context.xml w META-INF i dodaj allowCasualMultipartParsing = "true"

<?xml version="1.0" encoding="UTF-8"?>
<Context allowCasualMultipartParsing="true">
  <!-- empty or not depending your project -->
</Context>
Xavier Lambros
źródło
Jest to obejście problemu nieprawidłowej konfiguracji / kolejności filtrów.
BalusC
Cześć @BalusC, czy możesz podać nam więcej wyjaśnień? Czy jest lepszy sposób niż to obejście?
Xavier Lambros
Zobacz moją odpowiedź w tym pytaniu.
BalusC
0

Z JBoss 7.2 (Undertow) i PrimeFaces 6.0 org.primefaces.webapp.filter.FileUploadFilter powinien zostać usunięty z web.xml, a program do przesyłania parametrów kontekstu powinien być ustawiony na natywny:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>native</param-value>
</context-param>
Alex D.
źródło
Powinien? Czy otrzymujesz określone błędy, jeśli ich nie masz?
Kukeltje
Tak, moje FileUploadEvent nie jest wywoływane bez tych zmian.
Alex D
To nie jest wyraźny błąd, to nieoczekiwane zachowanie
Kukeltje