Cum se utilizează PrimeFaces p:fileUpload? Metoda Listener nu este niciodată invocată sau UploadedFile este nulă / aruncă o eroare / nu este utilizabilă (Programare, Jsf, Încărcare Fișiere, Jsf 2, Primefaces)

Rodrigo Cavalcante a intrebat.

Încerc să încarc un fișier folosind PrimeFaces, dar metoda fileUploadListener metoda nu este invocată după ce se termină încărcarea.

Iată care este vizualizarea:

<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 bean-ul:

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

}

Am plasat un breakpoint pe metodă, dar aceasta nu este niciodată apelată. Când se utilizează mode="simple" și ajax="false", este invocată, dar vreau să funcționeze în modul avansat. Folosesc Netbeans și Glassfish 3.1.

11 răspunsuri
BalusC

Cum se configurează și se rezolvă problemele <p:fileUpload> depinde de versiunea PrimeFaces și JSF.

Toate versiunile PrimeFaces

Cerințele de mai jos se aplică tuturor versiunilor PrimeFaces:

  1. enctype din atributul <h:form> trebuie să fie setat la multipart/form-data. Atunci când acesta lipsește, încărcarea ajax poate funcționa, dar comportamentul general al browserului este nespecificat și depinde de compoziția formularului și de marca/versiunea webbrowserului. Specificați-l întotdeauna pentru a fi în siguranță.

  2. Atunci când se utilizează mode="advanced" (adică încărcarea ajax, aceasta este opțiunea implicită), asigurați-vă că aveți un <h:head> în șablonul (principal). Acest lucru va asigura că fișierele JavaScript necesare sunt incluse în mod corespunzător. Acest lucru nu este necesar pentru mode="simple" (non-ajax upload), dar acest lucru ar strica aspectul și funcționalitatea tuturor celorlalte componente PrimeFaces, așa că oricum nu vreți să lipsească.

  3. Atunci când se utilizează mode="simple" (adică încărcare non-ajax), atunci ajax trebuie să fie dezactivat pe toate butoanele/legăturile de comandă PrimeFaces de către ajax="false", și trebuie să utilizați <p:fileUpload value> cu <p:commandButton action> în loc de <p:fileUpload listener>.

Așadar, dacă doriți încărcarea (automată) a fișierelor cu suport ajax (atenție la <h:head>!):

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

Sau dacă doriți o încărcare de fișiere non-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 transient UploadedFile uploadedFile; // +getter+setter

public void upload() {
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}

Rețineți că atributele legate de ajax, cum ar fi auto, allowTypes, update, onstart, oncomplete, etc. sunt ignorate în mode="simple". Așadar, este inutil să le specificați în acest caz.

De asemenea, rețineți că trebuie să citiți imediat conținutul fișierului în interiorul metodelor menționate mai sus și nu într-o altă metodă bean invocată de o cerere HTTP ulterioară. Acest lucru se datorează faptului că conținutul fișierului încărcat are domeniul de aplicare al cererii și, prin urmare, nu este disponibil într-o cerere HTTP ulterioară/diferită. Orice încercare de a-l citi într-o solicitare ulterioară se va termina cel mai probabil cu java.io.FileNotFoundException în fișierul temporar.


PrimeFaces 8.x

Configurația este identică cu informațiile de mai jos despre versiunea 5.x, dar dacă ascultătorul dvs. nu este apelat, verificați dacă atributul method este apelat listener și nu (ca în cazul versiunilor anterioare versiunii 8.x) fileUploadListener.


PrimeFaces 5.x

Aceasta face nu necesită nicio configurație suplimentară dacă utilizați JSF 2.2 și dacă nu este necesară o configurare suplimentară. faces-config.xml este, de asemenea, declarată conformă cu versiunea JSF 2.2. Trebuie să faceți nu nevoie deloc de filtrul de încărcare a fișierelor PrimeFaces și, de asemenea, nu aveți nu nevoie de filtrul primefaces.UPLOADER context din web.xml. În cazul în care nu vă este clar cum să instalați și să configurați corect JSF în funcție de serverul țintă utilizat, accesați secțiunea Cum se instalează și se configurează corect bibliotecile JSF prin Maven? și secțiunea „Instalarea JSF” din pagina noastră wiki JSF.

Dacă totuși nu folosiți încă JSF 2.2 și nu îl puteți actualiza (ar trebui să nu depună niciun efort atunci când sunteți deja pe un container compatibil cu Servlet 3.0), atunci trebuie să înregistrați manual filtrul de încărcare a fișierelor PrimeFaces de mai jos în web.xml (acesta va analiza cererea multiplă și va completa harta parametrilor cererii obișnuite, astfel încât FacesServlet poate continua să lucreze ca de obicei):

<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> valoarea din facesServlet trebuie să se potrivească exact cu valoarea din <servlet> de la intrarea din javax.faces.webapp.FacesServlet în aceeași web.xml. Deci, dacă este, de ex. Faces Servlet, atunci trebuie să o editați în consecință pentru a se potrivi.


PrimeFaces 4.x

Aceeași poveste ca la PrimeFaces 5.x se aplică și la 4.x.

Există doar o problemă potențială în obținerea conținutului fișierului încărcat de către UploadedFile#getContents(). Aceasta va returna null atunci când se utilizează API nativ în loc de Apache Commons FileUpload. Trebuie să utilizați UploadedFile#getInputStream() în schimb. Consultați și Cum se inserează imaginea încărcată din p:fileUpload ca BLOB în MySQL?

O altă problemă potențială cu API-ul nativ se va manifesta este atunci când componenta de încărcare este prezentă într-un formular pe care este lansată o altă cerere ajax „obișnuită” diferită care nu procesează componenta de încărcare. A se vedea, de asemenea, File upload doesn’t work with AJAX in PrimeFaces 4.0/JSF 2.2.x – javax.servlet.ServletException: The request content-type is not a multipart/form-data.

Ambele probleme pot fi rezolvate și prin trecerea la Apache Commons FileUpload. Consultați secțiunea PrimeFaces 3.x pentru detalii.


PrimeFaces 3.x

Această versiune nu acceptă încărcarea nativă de fișiere JSF 2.2 / Servlet 3.0. Trebuie să instalați manual Apache Commons FileUpload și să înregistrați în mod explicit filtrul de încărcare a fișierelor în web.xml.

Aveți nevoie de următoarele biblioteci:

Acestea trebuie să fie prezente în classpath-ul de execuție al aplicației web. Dacă folosiți Maven, asigurați-vă că acestea sunt cel puțin runtime scoped (sfera implicită de compilație este, de asemenea, bună). Atunci când transportați manual JAR-uri, asigurați-vă că acestea ajung în /WEB-INF/lib folder.

Detaliile de înregistrare a filtrului de încărcare a fișierelor pot fi găsite în secțiunea PrimeFaces 5.x de mai sus. În cazul în care utilizați PrimeFaces 4+ și doriți să utilizați în mod explicit Apache Commons FileUpload în loc de încărcarea nativă a fișierelor JSF 2.2 / Servlet 3.0, atunci aveți nevoie, pe lângă bibliotecile și filtrul menționate, și de parametrul de context de mai jos în web.xml:

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

Depanare

În cazul în care tot nu funcționează, iată alte posibile cauze care nu au legătură cu configurația PrimeFaces:

  1. Numai dacă utilizați filtrul de încărcare a fișierelor PrimeFaces: Există o altă Filter în aplicația dvs. web care rulează înainte de filtrul de încărcare a fișierelor PrimeFaces și a consumat deja corpul cererii, de exemplu, prin apelarea filtrului PrimeFaces. getParameter(), getParameterMap(), getReader(), etcetera. Corpul unei cereri poate fi analizat o singură dată. Dacă apelați una dintre aceste metode înainte ca filtrul de încărcare a fișierelor să își facă treaba, atunci filtrul de încărcare a fișierelor va primi un corp de cerere gol.

    Pentru a remedia acest lucru, ar trebui să puneți metoda <filter-mapping> a filtrului de încărcare a fișierelor înainte de celuilalt filtru în web.xml. Dacă cererea nu este un multipart/form-data request, atunci filtrul de încărcare a fișierelor va continua ca și cum nu s-ar fi întâmplat nimic. Dacă utilizați filtre care sunt adăugate automat deoarece utilizează adnotări (de exemplu, PrettyFaces), este posibil să fie necesar să adăugați o ordine explicită prin web.xml. Consultați Cum se definește ordinea de execuție a filtrelor servlet folosind adnotări în WAR

  2. Numai dacă utilizați filtrul de încărcare a fișierelor PrimeFaces: Există o altă opțiune Filter în aplicația dvs. web care rulează înainte de filtrul de încărcare a fișierelor PrimeFaces și care a efectuat un RequestDispatcher#forward() apel. De obicei, filtrele de rescriere a URL-urilor, cum ar fi PrettyFaces fac acest lucru. Acest lucru declanșează FORWARD dispatcher, dar filtrele ascultă în mod implicit pe REQUEST doar dispecerul.

    Pentru a remedia acest lucru, ar trebui fie să puneți filtrul de încărcare a fișierelor PrimeFaces înainte de filtrului de expediere, fie să reconfigurați filtrul de încărcare a fișierelor PrimeFaces pentru a asculta pe FORWARD și dispecerul:

     <filter-mapping>
         <filter-name>primeFacesFileUploadFilter</filter-name>
         <servlet-name>facesServlet</servlet-name>
         <dispatcher>REQUEST</dispatcher>
         <dispatcher>FORWARD</dispatcher>
     </filter-mapping>
    
  3. Există o funcție imbricata <h:form>. Acest lucru este ilegal în HTML, iar comportamentul browserului este nespecificat. De cele mai multe ori, browserul nu va trimite datele așteptate la trimiterea fișierului. Asigurați-vă că nu aveți un cuib <h:form>. Acest lucru este complet independent de forma enctype. Pur și simplu nu aninați deloc formulare.

Dacă încă aveți probleme, depanați traficul HTTP. Deschideți setul de instrumente pentru dezvoltatori al browserului web (apăsați F12 în Chrome/Firebug23+/IE9+) și verificați secțiunea Net/Network. Dacă partea HTTP pare în regulă, atunci depanați codul JSF. Puneți un punct de întrerupere pe FileUploadRenderer#decode() și avansați de acolo.


Salvarea fișierului încărcat

După ce ați reușit în sfârșit să funcționați, următoarea întrebare va fi probabil de genul „Cum/unde salvez fișierul încărcat?”. Ei bine, continuați aici: Cum se salvează fișierul încărcat în JSF.

Reinaldo Gil

Folosești și tu prettyfaces? Atunci setați dispecerul pe FORWARD:

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

Comentarii

  • Aceasta este încă o problemă atunci când este utilizată cu OCP Rewrite. Îți datorez o bere 🙂 –  > Por Babl.
user1791617

Un aspect pe care l-am observat cu Primefaces 3.4 și Netbeans 7.2:

Îndepărtați parametrii Netbeans autocompletați pentru funcția handleFileUpload, adică (event), altfel eventul ar putea fi nul.

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

HazeHorizon

Se pare că javax.faces.SEPARATOR_CHAR nu trebuie să fie egal cu _

Comentarii

  • Ați putea să detaliați, vă rog! –  > Por Karl Richter.
eric A

Am avut aceeași problemă cu primefaces 5.3 și am trecut prin toate punctele descrise de BalusC fără niciun rezultat. Am urmat sfatul său de a depana FileUploadRenderer#decode() și am descoperit că web.xml-ul meu era setat necorespunzător

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

Param-value trebuie să fie 1 din aceste 3 valori, dar nu toate!!! Întreaga secțiune context-param poate fi eliminată și valoarea implicită va fi auto

Waldeyr Mendes da Silva

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

@ViewScopedpublic class Submission implements 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>

Comentarii

  • Puteți explica de ce acesta este un răspuns? Este doar un cod, nu o explicație sau orice altceva –  > Por Kukeltje.
  • „#{bean.uploadFile}” vs „#{bean.uploadFasta}” , eliminați update=”messages” și va (doar) că funcționează pentru mine ! –  > Por romsky.
engilyin

Niciuna dintre sugestiile de aici nu a fost de ajutor pentru mine. Așa că am fost nevoit să depanez primefaces și am descoperit că motivul problemei era:

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

Apoi am adăugat secțiunea în servlet-ul meu faces în web.xml. Deci, asta a rezolvat problema:

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

Christian Altamirano Ayala

Am avut aceeași problemă, din cauza faptului că am avut toată configurația pe care o descriu în acest post, dar în cazul meu a fost pentru că am avut două importuri jquery (unul dintre ele a fost interogarea primefaces) care a cauzat conflicte pentru a încărca fișiere.

A se vedea conflictul Primefaces Jquery

Comentarii

  • Nu ai primit atunci o eroare specifică în consola de dezvoltare a browserului? –  > Por Kukeltje.
  • @Kukeltje asta este ceea ce a arătat consola: Uncaught TypeError: Object [object Object Object] has no method ‘fileupload’ –  > Por Christian Altamirano Ayala.
Xavier Lambros

Pentru cei care folosesc Tomee sau Tomcat și nu reușesc să facă să funcționeze, încercați să creați context.xml în META-INF și adăugați allowCasualMultipartParsing=”true”

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

Comentarii

  • Aceasta este o soluție de rezolvare pentru o configurare/ordonare greșită a filtrelor. –  > Por BalusC.
  • Bună @BalusC, ne poți da mai multe explicații? Există o modalitate mai bună decât această soluție de lucru? –  > Por Xavier Lambros.
  • Vedeți răspunsul meu la această întrebare. –  > Por BalusC.
Alex D

Cu JBoss 7.2(Undertow) și PrimeFaces 6.0 org.primefaces.webapp.filter.FileUploadFilter ar trebui să fie eliminat din web.xml și parametrul de context file uploader ar trebui să fie setat la nativ:

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

Comentarii

  • Ar trebui? Ai primit erori specifice dacă nu o faci? –  > Por Kukeltje.
  • Da, FileUploadEvent-ul meu nu se invocă fără aceste modificări. –  > Por Alex D.
  • Aceasta nu este o eroare explicită, ci un comportament neașteptat –  > Por Kukeltje.
A.Alessio

Puneți p:fileUpload la h:form a rezolvat problema în cazul meu.