Comment utiliser PrimeFaces p: fileUpload? La méthode d'écoute n'est jamais invoquée ou UploadedFile est nul / renvoie une erreur / non utilisable

101

J'essaie de télécharger un fichier à l'aide de PrimeFaces, mais la fileUploadListenerméthode n'est pas appelée une fois le téléchargement terminé.

Voici la vue:

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

Et le haricot:

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

}

J'ai placé un point d'arrêt sur la méthode, mais il n'est jamais appelé. Lorsque vous utilisez mode="simple"et ajax="false", il est appelé, mais je veux qu'il fonctionne en mode avancé. J'utilise Netbeans et Glassfish 3.1.

Rodrigo Cavalcante
la source

Réponses:

225

La configuration et le dépannage <p:fileUpload>dépendent de la version de PrimeFaces.

Toutes les versions de PrimeFaces

Les exigences ci-dessous s'appliquent à toutes les versions de PrimeFaces:

  1. L' enctypeattribut de <h:form>doit être défini sur multipart/form-data. Lorsque cela est absent, le téléchargement ajax peut simplement fonctionner, mais le comportement général du navigateur n'est pas spécifié et dépend de la composition du formulaire et de la création / version du navigateur Web. Précisez-le toujours pour être sûr.

  2. Lorsque vous utilisez mode="advanced"(c'est-à-dire le téléchargement ajax, c'est la valeur par défaut), assurez-vous que vous avez un <h:head>dans le modèle (maître). Cela garantira que les fichiers JavaScript nécessaires sont correctement inclus. Ce n'est pas nécessaire pour le mode="simple"(téléchargement non ajax), mais cela briserait l'apparence et la fonctionnalité de tous les autres composants PrimeFaces, vous ne voulez donc pas manquer cela de toute façon.

  3. Lors de l'utilisation mode="simple"(c.-à-d. Téléchargement non ajax), alors ajax doit être désactivé sur tous les boutons / liens de commande PrimeFaces par ajax="false", et vous devez utiliser <p:fileUpload value>avec <p:commandButton action>au lieu de <p:fileUpload fileUploadListener>(pour PrimeFaces <= 7.x) ou <p:fileUpload listener>(pour PrimeFaces> = 8.x)

Donc, si vous voulez un téléchargement (automatique) de fichiers avec le support ajax (attention <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!
}

Ou si vous souhaitez télécharger un fichier 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 UploadedFile uploadedFile; // +getter+setter

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

Notez que la attributs liés ajax tels que auto, allowTypes, update, onstart, oncomplete, etc sont ignorés dans mode="simple". Il est donc inutile de les préciser dans ce cas.

Notez également que vous devez lire le contenu du fichier immédiatement à l'intérieur des méthodes susmentionnées et non dans une méthode de bean différente appelée par une requête HTTP ultérieure. Ceci est dû au fait que le contenu du fichier téléchargé est limité à la demande et donc indisponible dans une requête HTTP ultérieure / différente. Toute tentative de lecture dans une demande ultérieure aboutira probablement java.io.FileNotFoundExceptionsur le fichier temporaire.


PrimeFaces 8.x

La configuration est identique aux informations de version 5.x ci-dessous, mais si votre auditeur n'est pas appelé, vérifiez si l'attriubute est appelé listeneret non (comme avec les versions antérieures à 8.x)fileUploadListener

PrimeFaces 5.x

Cela ne nécessite aucune configuration supplémentaire si vous utilisez JSF 2.2 et que votre version JSF 2.2 faces-config.xmlest également déclarée conforme. Vous n'avez pas du tout besoin du filtre de téléchargement de fichiers PrimeFaces. Dans le cas où vous ne savez pas comment installer et configurer correctement JSF en fonction du serveur cible utilisé, dirigez-vous vers Comment installer et configurer correctement les bibliothèques JSF via Maven? et la section "Installer JSF" de notre page wiki JSF .

Cependant, si vous n'utilisez pas encore JSF 2.2 et que vous ne pouvez pas le mettre à niveau (cela devrait être sans effort lorsque vous êtes déjà sur un conteneur compatible Servlet 3.0), vous devez enregistrer manuellement le filtre de téléchargement de fichier PrimeFaces ci-dessous web.xml(il analysera le multi demande de pièce et remplissez la carte des paramètres de la demande régulière afin que vous FacesServletpuissiez continuer à travailler comme d'habitude):

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

La <servlet-name>valeur de facesServletdoit correspondre exactement à la valeur de l' <servlet>entrée du javax.faces.webapp.FacesServletdans le même web.xml. Donc, si c'est par exemple Faces Servlet, vous devez le modifier en conséquence pour qu'il corresponde.


PrimeFaces 4.x

La même histoire que PrimeFaces 5.x s'applique également à 4.x.

Il n'y a qu'un problème potentiel pour obtenir le contenu du fichier téléchargé par UploadedFile#getContents(). Cela reviendra nulllorsque l'API native est utilisée à la place d'Apache Commons FileUpload. Vous devez utiliser à la UploadedFile#getInputStream()place. Voir aussi Comment insérer une image téléchargée depuis p: fileUpload en tant que BLOB dans MySQL?

Un autre problème potentiel avec l'API native se manifestera lorsque le composant de téléchargement est présent sous une forme sur laquelle une demande ajax "régulière" différente est déclenchée qui ne traite pas le composant de téléchargement. Voir aussi Le téléchargement de fichiers ne fonctionne pas avec AJAX dans PrimeFaces 4.0 / JSF 2.2.x - javax.servlet.ServletException: Le type de contenu de la demande n'est pas un multipart / form-data .

Les deux problèmes peuvent également être résolus en passant à Apache Commons FileUpload. Voir la section PrimeFaces 3.x pour plus de détails.


PrimeFaces 3.x

Cette version ne prend pas en charge le téléchargement de fichiers natifs JSF 2.2 / Servlet 3.0. Vous devez installer manuellement Apache Commons FileUpload et enregistrer explicitement le filtre de téléchargement de fichiers dans web.xml.

Vous avez besoin des bibliothèques suivantes:

Ceux-ci doivent être présents dans le chemin de classe d'exécution de l'application Web. Lorsque vous utilisez Maven, assurez-vous qu'ils ont au moins une portée d'exécution (la portée par défaut de la compilation est également bonne). Lorsque vous transportez manuellement des fichiers JAR, assurez-vous qu'ils se retrouvent dans un /WEB-INF/libdossier.

Le détail de l'enregistrement du filtre de téléchargement de fichiers peut être trouvé dans la section PrimeFaces 5.x ci-dessus. Dans le cas où vous utilisez PrimeFaces 4+ et que vous souhaitez utiliser explicitement Apache Commons FileUpload au lieu du téléchargement de fichier natif JSF 2.2 / Servlet 3.0, vous avez besoin à côté des bibliothèques mentionnées et filtrez également le paramètre de contexte ci-dessous dans web.xml:

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

Dépannage

Au cas où cela ne fonctionnerait toujours pas, voici une autre cause possible sans rapport avec la configuration de PrimeFaces:

  1. Seulement si vous utilisez le filtre de téléchargement de fichier PrimeFaces: Il y a une autre Filterdans votre webapp qui fonctionne avant le filtre de téléchargement de fichier PrimeFaces et a déjà consommé le corps de la demande en appelant par exemple getParameter(), getParameterMap(), getReader(), etc.. Un corps de requête ne peut être analysé qu'une seule fois. Lorsque vous appelez l'une de ces méthodes avant que le filtre de téléchargement de fichier ne fasse son travail, le filtre de téléchargement de fichier obtiendra un corps de requête vide.

    Pour résoudre ce problème, vous devez placer le <filter-mapping>filtre de téléchargement de fichiers avant l'autre filtre web.xml. Si la demande n'est pas une multipart/form-datademande, le filtre de téléchargement de fichiers continuera simplement comme si de rien n'était. Si vous utilisez des filtres qui sont ajoutés automatiquement parce qu'ils utilisent des annotations (par exemple PrettyFaces), vous devrez peut-être ajouter un ordre explicite via web.xml. Voir Comment définir l'ordre d'exécution du filtre de servlet à l'aide d'annotations dans WAR

  2. Uniquement si vous utilisez le filtre de téléchargement de fichiers PrimeFaces: il y en a un autre Filterdans votre application Web qui s'exécute avant le filtre de téléchargement de fichiers PrimeFaces et a effectué un RequestDispatcher#forward()appel. Habituellement, les filtres de réécriture d'URL tels que PrettyFaces le font. Cela déclenche le FORWARDrépartiteur, mais les filtres écoutent par défaut sur le REQUESTrépartiteur uniquement.

    Pour résoudre ce problème, vous devez soit placer le filtre de téléchargement de fichiers PrimeFaces avant le filtre de transfert, soit reconfigurer le filtre de téléchargement de fichiers PrimeFaces pour écouter également sur le FORWARDrépartiteur:

    <filter-mapping>
        <filter-name>primeFacesFileUploadFilter</filter-name>
        <servlet-name>facesServlet</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
    
  3. Il y a un imbriqué <h:form>. Ceci est illégal en HTML et le comportement du navigateur n'est pas spécifié. Plus que souvent, le navigateur n'enverra pas les données attendues lors de la soumission. Assurez-vous que vous n'emboitez pas <h:form>. Ceci est complètement indépendant de la forme enctype. N'emboitez pas du tout les formulaires.

Si vous rencontrez toujours des problèmes, déboguez le trafic HTTP. Ouvrez l'ensemble d'outils de développement du navigateur Web (appuyez sur F12 dans Chrome / Firebug23 + / IE9 +) et consultez la section Net / Network. Si la partie HTTP semble correcte, déboguez le code JSF. Mettez un point d'arrêt FileUploadRenderer#decode()et avancez à partir de là.


Enregistrement du fichier téléchargé

Une fois que vous l'avez finalement fait fonctionner, votre prochaine question sera probablement comme "Comment / où enregistrer le fichier téléchargé?". Eh bien, continuez ici: Comment enregistrer le fichier téléchargé dans JSF .

BalusC
la source
2
Une autre cause pourrait être que vous n'avez pas enregistré le filtre de téléchargement PrimeFaces web.xmlconformément au Guide de l'utilisateur PrimeFaces. L'avez-vous lu quand même? Cela n'expliquerait cependant pas pourquoi mode="simple"fonctionne pour vous.
BalusC
1
Oui je l'ai lu et j'ai enregistré le filtre, mais je viens de remarquer que cela me donne une erreur lors du démarrage du serveur "SEVERE: WebModule [/ EventsCalendary] PWC1270: Exception de départ du filtre PrimeFaces FileUpload Filter" Je me sens tellement stupide pour pas le remarquant avant. Des conseils pour résoudre cette erreur?
Rodrigo Cavalcante
2
Le mappage du filtre est peut-être incorrect. Il doit être mappé sur <servlet-name>le FacesServletcomme vous l'avez défini web.xml. La valeur par défaut de la plupart des IDE / générateurs de code est Faces Servlet, mais cela peut être aussi bon facesServletou quelque chose qui confirme davantage les conventions de dénomination.
BalusC
2
Nevermind l'a résolu en ajoutant commons-fileupload au classpath et commons-io, y a-t-il quelque part en train de dire que ces bibliothèques sont nécessaires? Quoi qu'il en soit, tout semble fonctionner en ce moment, la méthode est appelée comme elle était censée le faire, merci.
Rodrigo Cavalcante
1
Donc, tout est venu avec la situation que je fourmis la même configuration dans TomEE et JBoss .. forum.primefaces.org/viewtopic.php?f=3&t=43798
Dmitry Alexandrov
30

Vous utilisez aussi des prettyfaces? Ensuite, réglez le répartiteur sur FORWARD:

<filter-mapping>
   <filter-name>PrimeFaces FileUpload Filter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
   <dispatcher>FORWARD</dispatcher>
</filter-mapping>
Reinaldo Gil
la source
C'est toujours un problème lorsqu'il est utilisé avec OCP Rewrite. Je te dois une bière :)
Babl
7

Un point que j'ai remarqué avec Primefaces 3.4 et Netbeans 7.2:

Supprimez les paramètres auto-remplis Netbeans pour la fonction handleFileUpload ie (event) sinon l'événement pourrait être 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>

la source
2

On dirait que javax.faces.SEPARATOR_CHAR ne doit pas être égal à _

HazeHorizon
la source
2
Pourriez-vous élaborer, s'il vous plaît?!
Karl Richter
0

J'ai eu le même problème avec primefaces 5.3 et j'ai parcouru tous les points décrits par BalusC sans résultat. J'ai suivi ses conseils de débogage de FileUploadRenderer # decode () et j'ai découvert que mon web.xml n'était pas correctement défini

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

La valeur du paramètre doit être 1 de ces 3 valeurs mais pas toutes !! Toute la section context-param peut être supprimée et la valeur par défaut sera auto

Eric A
la source
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

La soumission de classe publique @ViewScoped implémente 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
la source
Pouvez-vous expliquer pourquoi c'est une réponse? Ce n'est que du code, pas une explication ou quoi que ce soit d'autre
Kukeltje
"# {bean.uploadFile}" vs "# {bean.uploadFasta}", supprimez update = "messages" et cela fonctionnera (seulement) pour moi!
romsky
0

Aucune des suggestions ici ne m'a été utile. J'ai donc dû déboguer les primefaces et j'ai trouvé que la raison du problème était:

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

Ensuite, j'ai ajouté une section dans mon servlet de visages dans le web.xml. Cela a donc résolu le problème:

<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
la source
0

J'ai eu le même problème, en raison du fait que j'avais toute la configuration décrite dans cet article, mais dans mon cas, c'était parce que j'avais deux importations jquery (l'une d'entre elles était la requête de primefaces), ce qui provoquait des conflits pour télécharger des fichiers.

Voir le conflit Primefaces Jquery

Christian Altamirano Ayala
la source
N'avez-vous pas eu une erreur spécifique dans la console de développement du navigateur?
Kukeltje
@Kukeltje c'est ce que la console a montré: Uncaught TypeError: Object [object Object] n'a pas de méthode 'fileupload'
Christian Altamirano Ayala
0

Pour les personnes utilisant Tomee ou Tomcat et ne pouvant pas le faire fonctionner, essayez de créer context.xml dans META-INF et ajoutez allowCasualMultipartParsing = "true"

<?xml version="1.0" encoding="UTF-8"?>
<Context allowCasualMultipartParsing="true">
  <!-- empty or not depending your project -->
</Context>
Xavier Lambros
la source
Il s'agit d'un contournement pour une mauvaise configuration / ordre de filtre.
BalusC
Salut @BalusC, pouvez-vous nous donner plus d'explications? Existe-t-il un meilleur moyen de contourner ce problème?
Xavier Lambros
Voir ma réponse dans cette question.
BalusC
0

Avec JBoss 7.2 (Undertow) et PrimeFaces 6.0 org.primefaces.webapp.filter.FileUploadFilter doit être supprimé de web.xml et le programme de téléchargement de fichiers de paramètres de contexte doit être défini sur natif:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>native</param-value>
</context-param>
Alex D
la source
Devrait? Avez-vous des erreurs spécifiques si vous ne le faites pas?
Kukeltje
Oui, mon FileUploadEvent ne s’appelle pas sans ces modifications.
Alex D
Ce n'est pas une erreur explicite, c'est un comportement inattendu
Kukeltje