Partage
  • Partager sur Facebook
  • Partager sur Twitter

Upload de fichier tronqué de 32ko

Sujet résolu
    30 novembre 2015 à 16:48:16

    Bonjour, j'ai suivi le cours Java EE, quand j'ai fait la partie sur l'upload de fichiers tout fonctionnait correctement. Maintenant que je reviens dessus, j'aperçois un truc assez bizarre, toute image que j'envoie, quand elle est écrite dans le répertoire de stockage de l'appli web, elle est systématiquement tronquée de 32ko (ce qui corrompt évidemment le fichier).... Aucune exception n'est levée, tout se déroule normalement....

    public static final int DEFAULT_BUFFER_SIZE = 10240; // 10 ko
    private static void ecrireFichier( InputStream contenu, String nomFichier, String chemin ) throws Exception {
            BufferedInputStream entree = null;
            BufferedOutputStream sortie = null;
            try {
                entree = new BufferedInputStream( contenu, Utils.DEFAULT_BUFFER_SIZE );
                sortie = new BufferedOutputStream( new FileOutputStream( new File( chemin + nomFichier ) ), Utils.DEFAULT_BUFFER_SIZE );
    
                byte[] tampon = new byte[Utils.DEFAULT_BUFFER_SIZE];
                int longueur = 0;
                while ( ( longueur = entree.read( tampon ) ) > 0 ) {
                	System.out.println(longueur);
                    sortie.write( tampon, 0, longueur );
                }
            } catch ( Exception e ) {
                throw new FormValidationException( "Erreur lors de l'écriture du fichier sur le disque." );
            } finally {
                try {
                    sortie.close();
                } catch ( IOException ignore ) {
                }
                try {
                    entree.close();
                } catch ( IOException ignore ) {
                }
            }
        }
    	public static String validerImage(HttpServletRequest req, HttpServlet servlet, String champImage) throws FormValidationException {
    		String nomFichier = null;
            
            try {
                Part part = req.getPart( champImage );
                
                nomFichier = extractFilename( part );
    
                if ( nomFichier != null && !nomFichier.isEmpty() ) {
                    nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 ).substring( nomFichier.lastIndexOf( '\\' ) + 1 );
    
                    InputStream contenuFichier = part.getInputStream();
                    
                    try {
                    	
                        validationType( new BufferedInputStream(contenuFichier), "image" );
                        
                        try {
                            ecrireFichier( contenuFichier, nomFichier, servlet.getServletConfig().getInitParameter( CONFIG_CHEMIN ));
                        } catch ( Exception e ) {
                        	throw new Exception( "Erreur lors de l'écriture du fichier sur le disque." );
                        }
                        
                    } catch ( Exception e ) {
                    	throw new FormValidationException( e.getMessage() );
                    }
                }
                
            } catch ( IllegalStateException e ) {
                .....
            }
    	
            return nomFichier;
    	}
    		<div>
    			<form method="post" action="<c:url value="/creationClient" /> " enctype="multipart/form-data">
    		
    				<label for="imageClient">Image</label>
    				<input type="file" id="imageClient" name="imageClient" />
    				<span class="erreur"> <c:out value="${ form.mapErreurs.imageClient }" /></span>
    			
    				<input type="submit" value="Valider" />
    				<input type="reset" value="Remettre à zéro" /> <br/>
    			
    			</form>
    		</div>
      <servlet>
        <servlet-name>CreationClient</servlet-name>
        <servlet-class>com.sdzee.tp.servlets.CreationClient</servlet-class>
        <init-param>
          <param-name>cheminUpload</param-name>
          <param-value>F:\workspace\Tmp\tp6\</param-value>
        </init-param>
        <multipart-config>
          <location>F:\workspace\Tmp\tp6\</location>
          <max-file-size>10485760</max-file-size>
          <max-request-size>52428800</max-request-size>
          <file-size-threshold>5242880</file-size-threshold>
        </multipart-config>
      </servlet>






    A moins d'avoir touché quelque chose qui devrait me sauter aux yeux, il s'agit d'une simple lecture/écriture. Mais comme il n'y a aucune exception côté Java, on dirait que c'est dans l'InputStream lui-même (que l'on récupère de la requête)que les 32ko sont manquants. Serait ce un problème de conf du serveur? A quoi ces 32ko pourraient correspondre?

    Merci

    -
    Edité par Ayok 30 novembre 2015 à 16:54:59

    • Partager sur Facebook
    • Partager sur Twitter
    Dans le doute: Reboot !!     ლ(ಠ益ಠ)ლ
      30 novembre 2015 à 17:20:35

      Bonjour.

      Pour copie du Stream utilise le try-with-resource ou encore mieux des bibliothèques utilitaires comme IOUtils (cela se fait en 1 ligne de code)
      Faire ça à la main produit un code bourré de bug si l'on ne maîtrise pas.

      As-tu réellement besoin de buffer pour manipuler tes Stream ?
      Si non, supprime tous les BufferedInputStream et manipule directement l'InputStream. 

      Pour manipuler des chemins de fichier, utiliser la classe File.
      Inutile de se casser le cerveau à manipuler les / ou \, il y a des classes qui font ça facilement sans bug. 

      Tous les catch du type de base Exception sont à proscrire.

      Quand tu auras corrigé tout ça on pourra voir plus clair dans ton programme.

      • Partager sur Facebook
      • Partager sur Twitter
      Angular 2 est l'avenir, jQuery c'est de la merde !!! - Java 8 c'est l'an 2016+ (programmez en 1 ligne)
        30 novembre 2015 à 17:54:42

        Merci beaucoup de t'être penché sur mon cas.

        Oui en fait il s'agit du code du cours Java EE c'est pour ça

        https://openclassrooms.com/courses/creez-votre-application-web-avec-java-ee/formulaires-l-envoi-de-fichiers

        Je pense qu'il a voulu être au plus proche du système pour la pédagogie, après effectivement dans la "réalité" on ne fait pas comme ça tu as tout à fait raison "Try-with-resources, classe mère Exception...).

        En revanche j'ai souvent lu qu'utiliser les BufferedInputStream étaient par soucis de performances, meilleurs que les simples InputStream, et que ça tenait plus de la bonne pratique on va dire.

        Oui aussi pour les "/", il l'a écrit à la main par simplicité je pense, la première étape aurait même été le FileSeparator, et après comme tu dis utiliser des classes utilitaires qui font ça bien.

        C'est comme faire un new() directement en passage de paramètres, je sais que c'est ultra moche mais c'était à titre de test pour le cours :p

        Alors ben je me suis dit que j'allais tout reprendre depuis le début, une JSP avec juste l'input "File", et j'ai fait une servlet avec du code "linéaire", en copiant-collant directement chaque fonction dans le "doPost", pour aller au plus simple et ne garder que l'essentiel... et ça a fonctionné....

        Finalement par tatonnage, j'ai convergé vers la fonction "validationType"

            public static void validationType( BufferedInputStream contenuFichier, String mimeType ) throws FormValidationException {
            	
                /* Extraction du type MIME du fichier depuis l'InputStream nommé "contenu" */
                MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" );
                Collection<?> mimeTypes = MimeUtil.getMimeTypes( contenuFichier );
                
                if ( !mimeTypes.toString().startsWith( mimeType )){
                	throw new FormValidationException("Le type du fichier doit être une IMAGE");
                }
            }

        (là pour le coup le BufferedInputStream est nécessaire, car en mettant un simple InputStream comme indiqué dans le cours cela provoque des bugs lol)

        qui, quand je colle ce code directement dans la fonction appelante, cela rétablit le fonctionnement normal

        	public static String validerImage(HttpServletRequest req, HttpServlet servlet, String champImage) throws FormValidationException {
        		String nomFichier = null;
                
                try {
                    Part part = req.getPart( champImage );
                    
                    nomFichier = extractFilename( part );
        
                    if ( nomFichier != null && !nomFichier.isEmpty() ) {
                        nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 ).substring( nomFichier.lastIndexOf( '\\' ) + 1 );
        
                        InputStream contenuFichier = part.getInputStream();
                        
                        try {
                        	
                            /* Extraction du type MIME du fichier depuis l'InputStream nommé "contenu" */
                            MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" );
                            Collection<?> mimeTypes = MimeUtil.getMimeTypes( contenuFichier );
                            
                            if ( !mimeTypes.toString().startsWith( "image" )){
                            	throw new FormValidationException("Le type du fichier doit être une IMAGE");
                            }
                            
                            try {
                                ecrireFichier( contenuFichier, nomFichier, servlet.getServletConfig().getInitParameter( CONFIG_CHEMIN ));
                            } catch ( Exception e ) {
                            	throw new Exception( "Erreur lors de l'écriture du fichier sur le disque." );
                            }
                            
                        } catch ( Exception e ) {
                        	throw new FormValidationException( e.getMessage() );
                        }
                    }
                    
                } catch ( IllegalStateException e ) {
                    ...
                }
        	
                return nomFichier;
        	}

        C'est la fonction "validerImage" de mon premier post, mais avec le code de "validationType" simplement sorti..... Je ne vois PAS-DU-TOUT le rapport, et le pourquoi du comment ça pourrait influencer l'écriture du fichier 0_0'

        Alors là c'est à n'y rien comprendre. Bon quelque part ça me rassure en me disant que c'état pas un truc évident qui m'échappait, mais une sort d'effet de bord, pas vraiment prévisible car pas trop logique pour moi, je ne vois pas le lien.

        Pour info ce bout de code utilise la bibliothèque "mime-util-2.1.3" afin de vérifier le type MIME du fichier uploadé.

        Merci encore

        PS: bon ben je viens de remettre l'appel de la fonction, donc le code du premier post, histoire de me convaincre que cela venait bien de là.... et ça fonctionne aussi -_-' 

        Pour résumer, code inchangé et ça fonctionne à nouveau..... le serveur doit déconner quelque part je vois que ça :/




        • Partager sur Facebook
        • Partager sur Twitter
        Dans le doute: Reboot !!     ლ(ಠ益ಠ)ლ
          30 novembre 2015 à 19:50:02

           Ayok a écrit:

          Oui en fait il s'agit du code du cours Java EE c'est pour ça

          https://openclassrooms.com/courses/creez-votre-application-web-avec-java-ee/formulaires-l-envoi-de-fichiers

          Je pense qu'il a voulu être au plus proche du système pour la pédagogie, après effectivement dans la "réalité" on ne fait pas comme ça tu as tout à fait raison "Try-with-resources, classe mère Exception...).

          En revanche j'ai souvent lu qu'utiliser les BufferedInputStream étaient par soucis de performances, meilleurs que les simples InputStream, et que ça tenait plus de la bonne pratique on va dire.

          Disons que le cours date un peu (Java 6 ?), et comme il est impossible de le mettre à jour alors le forum propose des mauvaises pratiques.
          Du coup les nouveautés qui permettent de faire gagner un temps fou n'y font pas... 

          La gestion des flux est quelque chose de complexe, c'est pour cela qu'il y a des fonctions pour faire le boulot simplement et proprement.
          Faire des read() soit même est quelque chose de bas niveau, que l'on fait généralement lorsque l'on en a le besoin. De même pour l'utilisation de BufferedInputStream : je doute que tu ais des problématiques de performance.

          • Partager sur Facebook
          • Partager sur Twitter
          Angular 2 est l'avenir, jQuery c'est de la merde !!! - Java 8 c'est l'an 2016+ (programmez en 1 ligne)
            1 décembre 2015 à 20:32:19

            oui oui c'est ce que j'ai cru comprendre, visiblement il y a des soucis au niveau de la prise en compte des remarques pour les cours et leur modification sur les topics du fofo  ^^ (peut être un domaine ou un hébergeur qui a été perdu et non accessible désormais .... c'est quand même ballot pour un site de tuto et de cours, j'espère qu'ils reprendront le fil sinon cela deviendra vite désuet :/ comme tu le soulignes d'ailleurs)

            A la base je viens du C++ en Génie Logiciel, donc tu imagines bien que le bas niveau est ce qui m'a été inculqué dès le départ justement (optimisation du code et des performances) c'est pour ça que ça ne me choque pas de l'utiliser, mais j'avoue qu'il faut carrément se mettre au niveau au fil des années, tu as tout à fait raison :D

            PS: bon au final, j'ai creusé le problème... c'est la bibliothèque de vérification de MimeType qui a bien un soucis... Dans le tuto il est indiqué de passer un InputStream, cela fonctionne en temps normal mais si on upload un type incorrect (dans mon cas j'ai testé un fichier HTML), au moment de l'instruction

                private static void validationType( InputStream is, String mimeType ) throws FormValidationException {
                	/* Extraction du type MIME du fichier depuis l'InputStream nommé "contenu" */
                    MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" );
                    BufferedInputStream bis = new BufferedInputStream(is);
                    Collection<?> mimeTypes = MimeUtil.getMimeTypes( is );
            ...

            cela renvoie l'exception

            "InputStream must support the mark() and reset() methods. "



            j'ai cherché sur le net le pourquoi du comment, et je suis tombé sur un autre forum (veuillez censurer le lien si cela n'est pas permis désolé)

            http://www.developpez.net/forums/d1500356/java/developpement-web-java/servlets-jsp/erreur-medsea-mimeutil-mimeexception-inputstream-must-support-the-mark/

            c'est pour cela que j'ai utilisé un BufferedInputStream d'ailleurs. Quand je mets ce code

                private static void validationType( InputStream is, String mimeType ) throws FormValidationException {
                	/* Extraction du type MIME du fichier depuis l'InputStream nommé "contenu" */
                    MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" );
                    BufferedInputStream bis = new BufferedInputStream(is);
                    Collection<?> mimeTypes = MimeUtil.getMimeTypes( bis );
            ...

            (la différence est sur la dernière ligne "bis" à la place de "is")

            Cela règle bien la question de l'exception, mais introduit également un effet de bord (le tronquage des 32ko du fichier), dont je ne comprends toujours pas le fonctionnement..... 

            -
            Edité par Ayok 1 décembre 2015 à 20:37:37

            • Partager sur Facebook
            • Partager sur Twitter
            Dans le doute: Reboot !!     ლ(ಠ益ಠ)ლ
              1 décembre 2015 à 21:14:05

              Je n'arrive pas à accéder aux sources de MimeUtil donc je ne vois pas plus de détails.
              Sipasser par un BufferedInputStream fonctionne alors cela doit surement être une solution.

              Alors je pense comprendre d'où cela viendrait (j'ai enfin regardé en "détail" ton code) : tu utilises le même InputStream pour la méthode MimeUtil.getMimeTypes et ecrireFichier.
              Or la méthode MimeUtil.getMimeTypes "consomme" une partie du flux, et donc lorsque ecrireFichier consommes le reste cela doit poser problème.

              2 essais à faire : 

              • Cloner ton InputStream initial (doublage de la consommation mémoire)
              • Utiliser les autres surcharges de MimeUtil.getMimeTypes pour ne pas passer l'InputStream (voir si c'est possible)
              • Partager sur Facebook
              • Partager sur Twitter
              Angular 2 est l'avenir, jQuery c'est de la merde !!! - Java 8 c'est l'an 2016+ (programmez en 1 ligne)

              Upload de fichier tronqué de 32ko

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown