702 votes

Comment télécharger des fichiers vers le serveur en utilisant JSP/Servlet ?

Comment puis-je télécharger des fichiers vers le serveur en utilisant JSP/Servlet ? J'ai essayé ceci :

<form action="upload" method="post">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

Cependant, je n'obtiens que le nom du fichier, pas son contenu. Lorsque j'ajoute enctype="multipart/form-data" a la <form> entonces request.getParameter() renvoie à null .

Au cours de mes recherches, je suis tombé sur Apache Common FileUpload . J'ai essayé ceci :

FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = upload.parseRequest(request); // This line is where it died.

Malheureusement, la servlet a lancé une exception sans message clair ni cause. Voici la trace de la pile :

SEVERE: Servlet.service() for servlet UploadServlet threw exception
javax.servlet.ServletException: Servlet execution threw an exception
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:313)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:637)

0 votes

Peut-être que cet article vous sera utile : baeldung.com/upload-file-servlet

1 votes

@Adam : Ils ont copié ma réponse et ont ajouté un tas de publicité par dessus pour essayer de gagner de l'argent avec. Ouais, grand article

1 votes

Non, en fait, rien n'a été copié. J'ai écrit le premier jet de cet article ainsi que le code supplémentaire. La documentation de référence de base peut être trouvée ici : commons.apache.org/proper/commons-fileupload/using.html (qui est lié et cité dans l'article). Les exemples sont partiellement repris du document de référence de base (qui est le but de la documentation de référence - c'est-à-dire être un point de référence) mais pas dans leur intégralité (notez que les docs de référence ne vont pas dans beaucoup de détails). Merci !

1232voto

BalusC Points 498232

Introduction

Pour parcourir et sélectionner un fichier à télécharger, vous avez besoin d'un fichier HTML. <input type="file"> dans le formulaire. Comme indiqué dans le Spécification HTML vous devez utiliser le POST et la méthode enctype du formulaire doit être défini comme suit "multipart/form-data" .

<form action="upload" method="post" enctype="multipart/form-data">
    <input type="text" name="description" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

Après la soumission d'un tel formulaire, les données binaires du formulaire multipart sont disponibles dans le corps de la requête en un format différent que lorsque le enctype n'est pas réglé.

Avant Servlet 3.0, l'API Servlet ne supportait pas nativement multipart/form-data . Il ne supporte que l'enctype de formulaire par défaut de application/x-www-form-urlencoded . El request.getParameter() et les consorts reviendraient tous null lors de l'utilisation de données de formulaire en plusieurs parties. C'est là que le célèbre Apache Commons FileUpload est entré en scène.

Ne l'analysez pas manuellement !

En théorie, vous pouvez analyser vous-même le corps de la requête en vous basant sur les éléments suivants ServletRequest#getInputStream() . Cependant, il s'agit d'un travail précis et fastidieux qui nécessite une connaissance précise des éléments suivants RFC2388 . Vous ne devez pas essayer de faire cela tout seul ou de copier-coller un code sans bibliothèque trouvé ailleurs sur Internet. De nombreuses sources en ligne ont échoué dans ce domaine, comme roseindia.net. Voir aussi téléchargement d'un fichier pdf . Vous devriez plutôt utiliser une vraie bibliothèque qui est utilisée (et implicitement testée !) par des millions d'utilisateurs depuis des années. Une telle bibliothèque a prouvé sa robustesse.

Si vous utilisez déjà Servlet 3.0 ou une version plus récente, utilisez l'API native.

Si vous utilisez au moins la version 3.0 de Servlet (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, etc.), vous pouvez utiliser l'API standard fournie. [HttpServletRequest#getPart()](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getParts()) pour collecter les éléments de données du formulaire multipartite (la plupart des implémentations de Servlet 3.0 utilisent en fait Apache Commons FileUpload sous couvert pour cela !) De plus, les champs de formulaire normaux sont disponibles par getParameter() de la manière habituelle.

Tout d'abord, annotez votre servlet avec @MultipartConfig afin de lui permettre de reconnaître et de soutenir multipart/form-data et ainsi obtenir getPart() pour travailler :

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    // ...
}

Ensuite, mettez en œuvre son doPost() comme suit :

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String description = request.getParameter("description"); // Retrieves <input type="text" name="description">
    Part filePart = request.getPart("file"); // Retrieves <input type="file" name="file">
    String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
    InputStream fileContent = filePart.getInputStream();
    // ... (do your job here)
}

Notez le Path#getFileName() . Il s'agit d'un correctif MSIE pour l'obtention du nom du fichier. Ce navigateur envoie incorrectement le chemin d'accès complet du fichier avec le nom au lieu du seul nom du fichier.

Au cas où vous auriez un <input type="file" name="file" multiple="true" /> pour le téléchargement de plusieurs fichiers, rassemblez-les comme ci-dessous (malheureusement, il n'existe pas de méthode telle que request.getParts("file") ) :

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // ...
    List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName()) && part.getSize() > 0).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">

    for (Part filePart : fileParts) {
        String fileName = Paths.get(filePart.getSubmittedFileName()).getFileName().toString(); // MSIE fix.
        InputStream fileContent = filePart.getInputStream();
        // ... (do your job here)
    }
}

Si vous n'êtes pas encore sur Servlet 3.1, obtenez manuellement le nom du fichier soumis

Notez que [Part#getSubmittedFileName()](https://docs.oracle.com/javaee/7/api/javax/servlet/http/Part.html#getSubmittedFileName()) a été introduit dans Servlet 3.1 (Tomcat 8, Jetty 9, WildFly 8, GlassFish 4, etc). Si vous n'êtes pas encore sur Servlet 3.1, alors vous avez besoin d'une méthode utilitaire supplémentaire pour obtenir le nom du fichier soumis.

private static String getSubmittedFileName(Part part) {
    for (String cd : part.getHeader("content-disposition").split(";")) {
        if (cd.trim().startsWith("filename")) {
            String fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
            return fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
        }
    }
    return null;
}

String fileName = getSubmittedFileName(filePart);

Notez la correction de MSIE concernant l'obtention du nom du fichier. Ce navigateur envoie incorrectement le chemin d'accès complet du fichier avec le nom au lieu du seul nom du fichier.

Si vous ne disposez pas encore de Servlet 3.0, utilisez Apache Commons FileUpload.

Si vous ne disposez pas encore de Servlet 3.0 (n'est-il pas temps de procéder à une mise à niveau ?), la pratique courante consiste à utiliser les éléments suivants Apache Commons FileUpload pour analyser les demandes de données de formulaires en plusieurs parties. Il dispose d'un excellent Guide de l'utilisateur y FAQ (parcourez attentivement les deux). Il y a aussi l'émission de O'Reilly (" cos ") MultipartRequest mais il présente quelques bogues (mineurs) et n'est plus activement maintenu depuis des années. Je ne recommanderais pas de l'utiliser. Apache Commons FileUpload est toujours activement maintenu et actuellement très mature.

Afin d'utiliser Apache Commons FileUpload, vous devez avoir au moins les fichiers suivants dans le répertoire de votre application web /WEB-INF/lib :

Votre première tentative a échoué, probablement parce que vous avez oublié l'IO commun.

Voici un exemple de lancement, comment le doPost() de votre UploadServlet peut ressembler lorsqu'on utilise Apache Commons FileUpload :

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
        for (FileItem item : items) {
            if (item.isFormField()) {
                // Process regular form field (input type="text|radio|checkbox|etc", select, etc).
                String fieldName = item.getFieldName();
                String fieldValue = item.getString();
                // ... (do your job here)
            } else {
                // Process form file field (input type="file").
                String fieldName = item.getFieldName();
                String fileName = FilenameUtils.getName(item.getName());
                InputStream fileContent = item.getInputStream();
                // ... (do your job here)
            }
        }
    } catch (FileUploadException e) {
        throw new ServletException("Cannot parse multipart request.", e);
    }

    // ...
}

Il est très important que vous n'appeliez pas getParameter() , getParameterMap() , getParameterValues() , getInputStream() , getReader() etc. sur la même requête au préalable. Sinon, le conteneur de servlets lira et analysera le corps de la requête et Apache Commons FileUpload obtiendra un corps de requête vide. Voir aussi a.o. ServletFileUpload#parseRequest(request) renvoie une liste vide. .

Notez le FilenameUtils#getName() . Il s'agit d'une correction de MSIE concernant l'obtention du nom du fichier. Ce navigateur envoie incorrectement le chemin d'accès complet du fichier avec le nom au lieu du seul nom du fichier.

Vous pouvez également envelopper tout cela dans un fichier Filter qui analyse tout cela automatiquement et remet le tout dans le parametermap de la requête afin que vous puissiez continuer à utiliser request.getParameter() de la manière habituelle et récupérer le fichier téléchargé par request.getAttribute() . Vous pouvez trouver un exemple dans cet article de blog .

Solution de contournement pour le bogue de GlassFish3 de getParameter() toujours en activité null

Notez que les versions de Glassfish plus anciennes que 3.1.2 avaient un bug dans lequel le getParameter() revient toujours null . Si vous ciblez un tel conteneur et que vous ne pouvez pas le mettre à niveau, vous devez alors extraire la valeur de getPart() à l'aide de cette méthode utilitaire :

private static String getValue(Part part) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(part.getInputStream(), "UTF-8"));
    StringBuilder value = new StringBuilder();
    char[] buffer = new char[1024];
    for (int length = 0; (length = reader.read(buffer)) > 0;) {
        value.append(buffer, 0, length);
    }
    return value.toString();
}

String description = getValue(request.getPart("description")); // Retrieves <input type="text" name="description">

Sauvegarde du fichier téléchargé (n'utilisez pas l'option getRealPath() ni part.write() !)

Consultez les réponses suivantes pour savoir comment sauvegarder correctement les données obtenues. InputStream (le fileContent comme indiqué dans les extraits de code ci-dessus) vers le disque ou la base de données :

Servir le fichier téléchargé

Consultez les réponses suivantes pour obtenir des détails sur la manière de renvoyer au client le fichier enregistré sur le disque ou dans la base de données :

Ajaxifier le formulaire

Consultez les réponses suivantes pour savoir comment télécharger en utilisant Ajax (et jQuery). Notez que le code du servlet qui collecte les données du formulaire n'a pas besoin d'être modifié pour cela ! Seule la façon dont vous répondez peut être modifiée, mais c'est plutôt trivial (c'est-à-dire qu'au lieu de transmettre à JSP, il suffit d'imprimer du JSON ou du XML ou même du texte brut en fonction de ce que le script responsable de l'appel Ajax attend).


J'espère que tout cela vous aidera :)

0 votes

Ah désolé, je voyais request.getParts("file") et j'étais confus x_x

0 votes

Avec Servlet 3.0, si un MultipartConfig est violée (ex : maxFileSize ), en appelant request.getParameter() renvoie null. Est-ce intentionnel ? Et si je récupère des paramètres normaux (texte) avant d'appeler getPart (et la vérification d'un IllegalStateException ) ? Cela provoque un NullPointerException avant que je n'aie la chance de vérifier si la IllegalStateException .

0 votes

@BalusC J'ai créé un post à ce sujet, avez-vous une idée de la façon dont je pourrais récupérer des informations supplémentaires à partir de l'API de fichier webKitDirectory. Plus de détails ici stackoverflow.com/questions/45419598/

13voto

chepe lucho Points 704

Sans composant ou bibliothèque externe dans Tomcat 6 ou 7

Activation du téléchargement dans le web.xml fichier :

http://joseluisbz.wordpress.com/2014/01/17/manually-installing-php-tomcat-and-httpd-lounge/#Enabling%20File%20Uploads .

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <multipart-config>
      <max-file-size>3145728</max-file-size>
      <max-request-size>5242880</max-request-size>
    </multipart-config>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

COMME VOUS POUVEZ LE VOIR :

    <multipart-config>
      <max-file-size>3145728</max-file-size>
      <max-request-size>5242880</max-request-size>
    </multipart-config>

Téléchargement de fichiers à l'aide de JSP. Fichiers :

Dans le fichier html

<form method="post" enctype="multipart/form-data" name="Form" >

  <input type="file" name="fFoto" id="fFoto" value="" /></td>
  <input type="file" name="fResumen" id="fResumen" value=""/>

Dans le fichier JSP o Servlet

    InputStream isFoto = request.getPart("fFoto").getInputStream();
    InputStream isResu = request.getPart("fResumen").getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte buf[] = new byte[8192];
    int qt = 0;
    while ((qt = isResu.read(buf)) != -1) {
      baos.write(buf, 0, qt);
    }
    String sResumen = baos.toString();

Modifiez votre code en fonction des exigences des servlets, comme taille maximale du fichier , Taille maximale des requêtes et d'autres options que vous pouvez définir...

11voto

Pranav Points 128

Vous avez besoin de la common-io.1.4.jar à inclure dans votre lib ou si vous travaillez dans un éditeur, comme NetBeans, vous devez aller dans les propriétés du projet et ajouter le fichier JAR et vous aurez terminé.

Pour obtenir le common.io.jar Il suffit de le googler ou d'aller sur le site d'Apache. Tomcat où vous avez l'option pour un téléchargement gratuit de ce fichier. Mais n'oubliez pas une chose : téléchargez le fichier ZIP binaire si vous êtes un utilisateur de Windows.

0 votes

Impossible de trouver .jar mais .zip . Voulez-vous dire .zip ?

9voto

J'utilise un servlet commun pour cada Html Form, qu'il ait des pièces jointes ou non. Cette servlet renvoie un TreeMap où les clés sont les paramètres de nom de jsp et les valeurs sont les entrées de l'utilisateur et enregistre toutes les pièces jointes dans un répertoire fixe et plus tard vous renommez le répertoire de votre choix.ici Connections est notre interface personnalisée ayant un objet de connexion. Je pense que cela vous aidera

public class ServletCommonfunctions extends HttpServlet implements
        Connections {

    private static final long serialVersionUID = 1L;

    public ServletCommonfunctions() {}

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException,
            IOException {}

    public SortedMap<String, String> savefilesindirectory(
            HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        // Map<String, String> key_values = Collections.synchronizedMap( new
        // TreeMap<String, String>());
        SortedMap<String, String> key_values = new TreeMap<String, String>();
        String dist = null, fact = null;
        PrintWriter out = response.getWriter();
        File file;
        String filePath = "E:\\FSPATH1\\2KL06CS048\\";
        System.out.println("Directory Created   ????????????"
            + new File(filePath).mkdir());
        int maxFileSize = 5000 * 1024;
        int maxMemSize = 5000 * 1024;
        // Verify the content type
        String contentType = request.getContentType();
        if ((contentType.indexOf("multipart/form-data") >= 0)) {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // maximum size that will be stored in memory
            factory.setSizeThreshold(maxMemSize);
            // Location to save data that is larger than maxMemSize.
            factory.setRepository(new File(filePath));
            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload(factory);
            // maximum file size to be uploaded.
            upload.setSizeMax(maxFileSize);
            try {
                // Parse the request to get file items.
                @SuppressWarnings("unchecked")
                List<FileItem> fileItems = upload.parseRequest(request);
                // Process the uploaded file items
                Iterator<FileItem> i = fileItems.iterator();
                while (i.hasNext()) {
                    FileItem fi = (FileItem) i.next();
                    if (!fi.isFormField()) {
                        // Get the uploaded file parameters
                        String fileName = fi.getName();
                        // Write the file
                        if (fileName.lastIndexOf("\\") >= 0) {
                            file = new File(filePath
                                + fileName.substring(fileName
                                        .lastIndexOf("\\")));
                        } else {
                            file = new File(filePath
                                + fileName.substring(fileName
                                        .lastIndexOf("\\") + 1));
                        }
                        fi.write(file);
                    } else {
                        key_values.put(fi.getFieldName(), fi.getString());
                    }
                }
            } catch (Exception ex) {
                System.out.println(ex);
            }
        }
        return key_values;
    }
}

0 votes

@buhake sindi hey, quel doit être le chemin du fichier si j'utilise un serveur en direct ou si je vis mon projet en téléchargeant des fichiers sur le serveur.

2 votes

N'importe quel répertoire dans le serveur live. Si vous écrivez un code pour créer un répertoire dans la servlet, le répertoire sera créé dans le serveur live.

6voto

Une autre source de ce problème se présente si vous utilisez Geronimo avec son Tomcat intégré. Dans ce cas, après de nombreuses itérations de test de commons-io et commons-fileupload, le problème provient d'un classloader parent qui manipule les jars commons-xxx. Cela doit être évité. Le crash se produit toujours à :

fileItems = uploader.parseRequest(request);

Notez que le type de liste de fileItems a changé avec la version actuelle de commons-fileupload pour être spécifiquement List<FileItem> par opposition aux versions antérieures où il était générique List .

J'ai ajouté le code source de commons-fileupload et de commons-io dans mon projet Eclipse afin de retracer l'erreur réelle et j'ai finalement obtenu un aperçu. Premièrement, l'exception levée est de type Throwable et non pas de type FileIOException ou même Exception (ces exceptions ne seront pas piégées). Deuxièmement, le message d'erreur est obscur en ce sens qu'il indique que la classe n'a pas été trouvée parce que axis2 n'a pas pu trouver commons-io. Axis2 n'est pas du tout utilisé dans mon projet mais existe en tant que dossier dans le sous-répertoire du dépôt Geronimo dans le cadre de l'installation standard.

Finalement, j'ai trouvé un endroit qui proposait une solution fonctionnelle qui a résolu mon problème avec succès. Vous devez masquer les jars du chargeur parent dans le plan de déploiement. Ceci a été mis dans geronimo-web.xml avec mon fichier complet montré ci-dessous.

Pasted from <http://osdir.com/ml/user-geronimo-apache/2011-03/msg00026.html> 

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<web:web-app xmlns:app="http://geronimo.apache.org/xml/ns/j2ee/application-2.0" xmlns:client="http://geronimo.apache.org/xml/ns/j2ee/application-client-2.0" xmlns:conn="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2" xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2" xmlns:ejb="http://openejb.apache.org/xml/ns/openejb-jar-2.2" xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-2.0" xmlns:name="http://geronimo.apache.org/xml/ns/naming-1.2" xmlns:pers="http://java.sun.com/xml/ns/persistence" xmlns:pkgen="http://openejb.apache.org/xml/ns/pkgen-2.1" xmlns:sec="http://geronimo.apache.org/xml/ns/security-2.0" xmlns:web="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1">
    <dep:environment>
        <dep:moduleId>
            <dep:groupId>DataStar</dep:groupId>
            <dep:artifactId>DataStar</dep:artifactId>
            <dep:version>1.0</dep:version>
            <dep:type>car</dep:type>
        </dep:moduleId>

<!--Don't load commons-io or fileupload from parent classloaders-->
        <dep:hidden-classes>
            <dep:filter>org.apache.commons.io</dep:filter>
            <dep:filter>org.apache.commons.fileupload</dep:filter>
        </dep:hidden-classes>
        <dep:inverse-classloading/>        

    </dep:environment>
    <web:context-root>/DataStar</web:context-root>
</web:web-app>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X