web-dev-qa-db-fra.com

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

J'essaie de télécharger un fichier à l'aide de PrimeFaces, mais la méthode fileUploadListener 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 celle-ci n'a jamais été appelée. Lorsque vous utilisez mode="simple" et ajax="false", il a été appelé, mais je veux que cela fonctionne en mode avancé. J'utilise Netbeans et Glassfish 3.1.

93
Rodrigo Cavalcante

Comment configurer et dépanner <p:fileUpload> dépend de la version de PrimeFaces.

Toutes les versions de PrimeFaces

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

  1. L'attribut enctype du <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 version/construction du navigateur Web. Il suffit de toujours spécifier qu'il faut être prudent.

  2. Lorsque vous utilisez mode="advanced" (c'est-à-dire le téléchargement ajax, il s'agit de 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. Ceci n'est pas requis pour mode="simple" (téléchargement non ajax), mais cela casserait l'apparence et la fonctionnalité de tous les autres composants PrimeFaces, vous ne voudriez donc pas le manquer de toute façon.

  3. Lorsque vous utilisez mode="simple" (envoi non ajax), ajax doit être désactivé sur 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>.

Donc, si vous voulez charger (automatiquement) un fichier avec le support ajax (attention au <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!
}

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 les attributs liés à ajax tels que auto, allowTypes, update, onstart, oncomplete, etc sont ignoré dans mode="simple". Il est donc inutile de les spécifier dans un tel cas.

Notez également que vous devez lire le contenu du fichier immédiatement dans les méthodes susmentionnées et non dans une méthode de bean différente invoquée par une requête HTTP ultérieure. Cela est dû au fait que le contenu du fichier téléchargé est limité à la demande et n'est donc pas disponible dans une demande HTTP ultérieure/différente. Toute tentative de lecture ultérieure dans une requête aboutira probablement à Java.io.FileNotFoundException dans le fichier temporaire.


PrimeFaces 5.x

Cela ne nécessite aucune configuration supplémentaire si vous utilisez JSF 2.2 et que votre faces-config.xml est également déclaré conforme à la version JSF 2.2. Vous n'avez pas du tout besoin du filtre de téléchargement de fichier PrimeFaces. Au cas où vous ne sauriez pas comment installer et configurer correctement JSF en fonction du serveur cible utilisé, allez à Comment installer et configurer correctement les bibliothèques JSF via Maven? et section "Installation de JSF" de notre page wiki JSF .

Si toutefois vous n'utilisez pas encore JSF 2.2 et que vous ne pouvez pas le mettre à niveau (cela devrait se faire sans effort lorsque vous êtes déjà sur un conteneur compatible Servlet 3.0), vous devez alors enregistrer manuellement le filtre de téléchargement de fichier PrimeFaces ci-dessous dans web.xml (il analysera le fichier en plusieurs parties et remplissez la mappe de paramètres de la demande régulière afin que FacesServlet puisse continuer à fonctionner 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 valeur <servlet-name> de facesServlet doit correspondre exactement à la valeur de l'entrée <servlet> du javax.faces.webapp.FacesServlet dans le même web.xml. Donc si c'est par exemple Faces Servlet, vous devez alors le modifier en conséquence.


PrimeFaces 4.x

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

Il n’ya qu’un problème potentiel à obtenir le contenu du fichier téléchargé par UploadedFile#getContents(). Cela renverra null lorsque l'API native sera utilisée à la place d'Apache Commons FileUpload. Vous devez utiliser UploadedFile#getInputStream() à la place. Voir aussi Comment insérer une image téléchargée depuis p: fileUpload sous forme de BLOB dans MySQL?

Un autre problème potentiel avec l'API native se manifestera lorsque le composant de téléchargement est présent dans un formulaire sur lequel une demande ajax "normale" différente est déclenchée, ce qui ne traite pas le composant de téléchargement. Voir aussi Le téléchargement de fichier 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 une donnée 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 fichier natif JSF 2.2/Servlet 3.0. Vous devez installer manuellement Apache Commons FileUpload et enregistrer explicitement le filtre de téléchargement de fichier dans web.xml.

Vous avez besoin des bibliothèques suivantes:

Ceux-ci doivent être présents dans le classpath d'exécution de l'application Web. Lors de l'utilisation de Maven, assurez-vous qu'ils sont au moins à la portée de l'exécution (l'étendue par défaut de la compilation est également bonne). Lorsque vous transportez manuellement des fichiers JAR, assurez-vous qu'ils se trouvent dans le dossier /WEB-INF/lib.

Le détail de l'enregistrement du filtre de téléchargement de fichier se trouve dans la section PrimeFaces 5.x ci-dessus. Si 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 devez ensuite sélectionner 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

Si cela ne fonctionne toujours pas, voici une autre cause possible non liée à la configuration de PrimeFaces:

  1. Seulement si vous utilisez le filtre de téléchargement de fichier PrimeFaces: Il existe un autre Filter dans votre application Web qui s'exécute avant le filtre de téléchargement de fichier PrimeFaces et a déjà consommé le corps de la demande par par exemple appelez 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> du filtre d'envoi de fichier avant l'autre filtre dans web.xml. Si la demande n'est pas une demande multipart/form-data, le filtre de téléchargement de fichier continuera comme si de rien n'était. Si vous utilisez des filtres ajoutés automatiquement, car 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. Seulement si vous utilisez le filtre de téléchargement de fichiers PrimeFaces: Il existe un autre Filter dans votre application Web qui s'exécute avant le filtre de téléchargement de fichiers PrimeFaces et qui a exécuté un RequestDispatcher#forward() call. Habituellement, les filtres de réécriture d'URL tels que PrettyFaces font ceci. Cela déclenche le répartiteur FORWARD, mais les filtres écoutent par défaut sur le répartiteur REQUEST uniquement.

    Pour résoudre ce problème, vous devez soit placer le filtre de téléchargement de fichier PrimeFaces avant le filtre de transfert, soit reconfigurer le filtre de téléchargement de fichier PrimeFaces pour l'écouter sur FORWARD dispatcher. aussi:

    <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 <h:form> imbriqué. Ceci est illégal en HTML et le comportement du navigateur n'est pas spécifié. Plus souvent, le navigateur n'envoie pas les données attendues lors de la soumission. Assurez-vous de ne pas imbriquer <h:form>. Ceci est complètement indépendant de la forme enctype de la forme. Il suffit de ne pas imbriquer les formes du tout.

Si vous avez toujours des problèmes, bien, déboguez le trafic HTTP. Ouvrez le jeu d’outils de développement du navigateur Web (appuyez sur F12 dans Chrome/Firebug23 +/IE9 +) et vérifiez la section Réseau/Réseau. Si la partie HTTP semble correcte, déboguez le code JSF. Placez un point d'arrêt sur FileUploadRenderer#decode() et avancez à partir de là.


Enregistrement du fichier téléchargé

Après que vous ayez enfin réussi à le faire fonctionner, votre prochaine question sera probablement: "Comment/où puis-je sauvegarder le fichier téléchargé?". Eh bien, continuez ici: Comment enregistrer un fichier téléchargé dans JSF .

211
BalusC

Vous utilisez aussi prettyfaces? Puis réglez le répartiteur sur AVANT:

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

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

Supprimez les paramètres Netbeans remplis automatiquement pour la fonction handleFileUpload i.e. (événement), 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>
6
user1791617

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

2
HazeHorizon

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 son conseil de débogage de FileUploadRenderer # decode () et j'ai découvert que mon fichier web.xml était mal configuré.

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

La valeur param 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 seraauto

0
eric A

Aucune des suggestions ici ne m'a été utile. J'ai donc dû déboguer Primefaces et la raison du problème était la suivante:

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

Ensuite, j'ai ajouté une section dans le servlet de mon visage dans le fichier web.xml. Donc, cela a 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>
0
engilyin

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

Voir Primefaces Jquery conflict

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

Pour ceux qui utilisent Tomee ou Tomcat et ne peuvent pas le faire fonctionner, essayez de créer context.xml in META-INF et ajoutez allowCasualMultipartParsing = "true"

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