Dreamisle.net Blog

Java, Rails, Security and so many ...

Utilisation Du rich:fileUpload

| Comments

Un certain nombre de site ont besoin de permettre aux utilisateurs d’envoyer des fichiers. Beaucoup utilisent le s:fileUpload, ou d’autres méthodes avec un champ html classique.

Mes recherches pour l’ajout d’un tel composant m’ont amenés vers un composant richFaces très bien fait : le rich:fileUpload Cependant ce composant est peu documenté et donc pas évident à mettre en oeuvre, c’est pourquoi j’ai pensé qu’un petit article à ce propos serait utile.

Tout d’abord côte vue JSF : le composant doit être placé dans un formulaire, donc classiquement on va avoir par exemple :

  

  <rich:panel>
    <f:facet name="header">Selectionnez les fichiers a importer</f:facet>
      <h:form id="uploadForm">
      <h:panelGrid columns="2" columnClasses="top,top" cellpadding="1" cellspacing="1">
      <rich:fileUpload id="upload"
            fileUploadListener="#{importClientService.fileUploadListener}"
            maxFilesQuantity="5"
            acceptedTypes="csv, txt">
            <a4j:support event="onuploadcomplete" reRender="fileListPanel" />
      </rich:fileUpload>
      <h:panelGroup id="fileListPanel">
        <rich:panel>
        <f:facet name="header">Fichiers téléchargés :</f:facet>
        <rich:dataGrid id="filegrid" value="#{importClientService.availableFileList}" var="file">
              <h:panelGrid columns="2">
                <h:selectBooleanCheckbox value="#{file.selected}" />
                <h:outputText value="#{file.fileName}" />
              </h:panelGrid>
        </rich:dataGrid>
        </rich:panel>
      </h:panelGroup>
      </h:panelGrid>
    </h:form>
  </rich:panel>

Ici on a défini le nombre de fichiers uplodables dans la liste à 5, on accepte que des fichiers de type csv ou txt, et on redessine le panel contenant la liste des fichiers uploadés grâce a un a4j:support :

<a4j:support event="onuploadcomplete" reRender="fileListPanel" />

A chaque fois que le composant fileupload recevera l’evenement “uploadComplete” il redessinera le panel fileListPanel, ainsi la liste sera rafraichie automatiquement en ajax.

Maintenant le problème est de savoir quoi faire de ça : "#{uploadBean.fileUploadListener}"

La documentation richfaces n’étant pas très explicite, j’ai pensé que cet article pourrait donc être utile

Déjà je vous conseille largement un composant stateful (ejb ou seam) en scope conversation pour votre bean chargé de gérer cela.

ensuite voila un exemple de code pour la méthode en question dans le bean uploadBean, ici cette méthode récupère le fichier téléchargé, et le recopie dans un répertoire choisi : Les explications dans les commentaires. La methode doit absolument prendre un UploadEvent en paramètre, c’est lui qui contient les données dont vous avez besoin pour être à l’ecoute des evenement d’upload.

/**
* file upload listener.
* @param event the upload event
* @throws IOException
*/
public void fileUploadListener(UploadEvent event) {
  if (event == null) {
    logger.warn("Invalid upload event");
  } else {
    // on recupere l'item envoye
    UploadItem uploadItem = event.getUploadItem(); 
    if (uploadItem.getFile().isFile()) { // si c'est bien un fichier
      String repo = PropertyManager.getInstance().getProperty("importRepository"); 
      // a vous d'adapter a votre 
      //code ici on recupere le path vers le repertoire ou stocker le fichier
      File fileToWrite = new File(repo + File.separator +  uploadItem.getFileName()); 
      // on construit un Fichier avec le path/nomdufichierrecu
      File uploadedFile = uploadItem.getFile();
      FileChannel in = null; // on va utiliser deux files channel pour faire la recopie
      FileChannel out = null;
      try {
        in = new FileInputStream(uploadedFile).getChannel();
        out = new FileOutputStream(fileToWrite).getChannel();
        in.transferTo(0, in.size(), out); 
       // on recopie le fichier en entree dans le repertoire voulu
        availableFileList.add(new FileUpload(fileToWrite.getName(), false));
       // ici FileUpload est une classe interne
       // au projet contenant simplement les donnees propres au fichier 
        //mais on porurait ajouter le nom simplement a 
        //une liste de String
        logger.debug("ADDED : #0", availableFileList.get(availableFileList.size() - 1).getFileName());
        logger.debug("fileUploadListener : LIST SIZE #0", availableFileList.size());
      } catch (IOException ex1) { 
        // recuperations des exceptions possibles
        logger.error("Error while copying the file.");
      } finally { // fermeture des filechannel
         if (in != null) {
           try {
             in.close(); 
           } catch (IOException ex2) {
             logger.error("Can't close input file channel");
           }
         if (out != null) {
           try {
             out.close();
           } catch (IOException ex3) {
              logger.error("Can't close ouput file channel");
           }
         }
       }
     }
   }
 }
}
 

Et voilà, vous avez un exemple de Listener pour le composant richfileupload, à vous maintenant d’adapter cette méthodes à vos projets et besoins. La documentations API de richfaces vous donneras les informations nécessaire pour recuperer ce que vous souhaitez dans les UploadItem.

Néanmoins il est possibles que Richfaces sur certaines versions vous pose des problèmes avec ses filtres, si vous tentez de recuperer directement le tableau byte [] data dans l’objet UploadEvent. Lorsqu’on utilise le getData il semblerait qu’on tente d’accéder directement à un fichier temporaire sur lequel on a pas les droits. Il semblerait qu’une solution soit de configurer le filtre Seam de cette manière afin de l’empecher de creer des fichiers dans temporaire là ou la jvm ne pourra pas les lires (adapter la maxRequestSize a vos besoin).

<filter>
        <filter-name>Seam Filter</filter-name>
        <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
        <init-param>
                <param-name>createTempFiles</param-name>
                <param-value>false</param-value>
        </init-param>
        <init-param>
                <param-name>maxRequestSize</param-name>
                <param-value>200000</param-value>
        </init-param>
</filter>

Cette configuration de filtre devrait règler le problème si vous voulez vraiment utiliser getData(). Cependant, il semble plus sur d’utiliser le getFile et de le transformer en byteArray ou d’utiliser la méthode de recopie de votre choix, par exemple celle utilisée ci dessus.

Et voilà en espérant avoir éclairci le fonctionnement de ce composant …