web-dev-qa-db-fra.com

Comment télécharger des fichiers sur un serveur en utilisant JSP / Servlet?

Comment puis-je télécharger des fichiers sur le serveur à l'aide de 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 ne reçois que le nom du fichier, pas le contenu du fichier. Lorsque j'ajoute enctype="multipart/form-data" au <form>, alors request.getParameter() renvoie null.

Au cours de la recherche, 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, le servlet a lancé une exception sans message clair et sans cause. Voici le stacktrace:

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)
668
Thang Pham

Introduction

Pour parcourir et sélectionner un fichier à télécharger, vous avez besoin d'un champ HTML _<input type="file">_ dans le formulaire. Comme indiqué dans la spécification HTML , vous devez utiliser la méthode POST et l'attribut enctype du formulaire doit être défini. à _"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 avoir soumis un tel formulaire, les données du formulaire multipart binaire sont disponibles dans le corps de la demande dans sous un format différent que lorsque la enctype n'est pas ensemble.

Avant Servlet 3.0, l'API Servlet ne supportait pas nativement _multipart/form-data_. Il ne prend en charge que le type de formulaire par défaut _application/x-www-form-urlencoded_. request.getParameter() et consorts renverraient tous null lors de l’utilisation de données de formulaire en plusieurs parties. C’est là que le bien connu Apache Commons FileUpload est entré en scène.

Ne pas analyser manuellement!

Vous pouvez théoriquement analyser la requête vous-même en vous basant sur ServletRequest#getInputStream() . Cependant, il s’agit d’un travail précis et fastidieux qui nécessite une connaissance précise de RFC2388 . Vous ne devriez pas essayer de le faire vous-même ou copypassez du code local sans bibliothèque trouvé ailleurs sur Internet. De nombreuses sources en ligne ont échoué, comme roseindia.net. Voir aussi téléchargement du fichier pdf . Vous devriez plutôt utiliser une vraie bibliothèque qui est utilisée (et implicitement testée!) Par des millions d'utilisateurs pendant des années. Une telle bibliothèque a prouvé sa robustesse.

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

Si vous utilisez au moins Servlet 3.0 (Tomcat 7, Jetty 9, JBoss AS 6, GlassFish 3, etc.), vous pouvez simplement utiliser l'API standard fournie HttpServletRequest#getPart() pour collecter les éléments de données de formulaire multipart individuels (la plupart des implémentations de Servlet 3.0 utilisent réellement Apache Commons FileUpload sous la couverture pour cela!). De plus, les champs de formulaire normaux sont disponibles par getParameter() de la manière habituelle.

Annotez d’abord votre servlet avec @MultipartConfig afin de le laisser reconnaître et prendre en charge les demandes _multipart/form-data_ et ainsi obtenir getPart():

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

Ensuite, implémentez sa 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 la Path#getFileName(). C'est un correctif MSIE quant à l'obtention du nom de fichier. Ce navigateur envoie de manière incorrecte le chemin d'accès complet au fichier au lieu du seul nom de fichier.

Si vous avez un _<input type="file" name="file" multiple="true" />_ pour le téléchargement de plusieurs fichiers, récupérez-les comme suit (malheureusement, la méthode request.getParts("file") n'existe pas):

_protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // ...
    List<Part> fileParts = request.getParts().stream().filter(part -> "file".equals(part.getName())).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)
    }
}
_

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

Notez que Part#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, 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 le correctif MSIE pour obtenir le nom du fichier. Ce navigateur envoie de manière incorrecte le chemin d'accès complet au fichier au lieu du seul nom de fichier.

Lorsque vous n'êtes pas encore sur Servlet 3.0, utilisez Apache Commons FileUpload

Si vous n'êtes pas encore sur Servlet 3.0 (n'est-il pas temps de mettre à niveau?), La pratique courante consiste à utiliser Apache Commons FileUpload pour analyser les demandes de données de formulaire multpart. Il a un excellent Guide de l'utilisateur et FAQ (passez avec soin tous les deux). Il y a aussi le O'Reilly ("( cos ") MultipartRequest, mais il a quelques bugs (mineurs) et n'est plus maintenu activement. pendant des années. Je ne recommanderais pas de l'utiliser. Apache Commons FileUpload est toujours activement maintenu et actuellement très mature.

Pour utiliser Apache Commons FileUpload, vous devez avoir au moins les fichiers suivants dans votre application web _/WEB-INF/lib_:

Votre tentative initiale a probablement échoué parce que vous avez oublié l’IO commun.

Voici un exemple de démarrage à quoi ressemble la doPost() de votre UploadServlet lors de l'utilisation d'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 auparavant getParameter(), getParameterMap(), getParameterValues(), getInputStream(), getReader(), etc., à la même demande. Sinon, le conteneur de servlet lira et analysera le corps de la requête. Apache Commons FileUpload obtiendra un corps de requête vide. Voir aussi a.o. ServletFileUpload # parseRequest (request) renvoie une liste vide .

Notez la FilenameUtils#getName(). C'est un correctif MSIE quant à l'obtention du nom de fichier. Ce navigateur envoie de manière incorrecte le chemin d'accès complet au fichier au lieu du seul nom de fichier.

Alternativement, vous pouvez également envelopper tout cela dans un Filter qui analyse tout automatiquement et replace le contenu dans la carte paramétrique de la demande afin que vous puissiez continuer à utiliser request.getParameter() comme d'habitude 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 GlassFish3 de getParameter() renvoyant toujours null

Notez que les versions de Glassfish antérieures à la version 3.1.2 contenaient un bogue dans lequel la getParameter() renvoie toujours null. Si vous ciblez un tel conteneur et ne pouvez pas le mettre à niveau, vous devez extraire la valeur de getPart() à l'aide de cette méthode d'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">
_

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

Reportez-vous aux réponses suivantes pour plus de détails sur la sauvegarde de la InputStream obtenue (la variable fileContent obtenue dans les extraits de code ci-dessus) sur le disque ou dans la base de données:

Servir le fichier téléchargé

Consultez les réponses suivantes pour plus de détails sur la manière de servir correctement le fichier sauvegardé depuis le disque ou la base de données au client:

Ajaxifier la forme

Allez dans les réponses suivantes pour télécharger en utilisant Ajax (et jQuery). Notez que le code de servlet pour collecter les données de formulaire n'a pas besoin d'être modifié pour cela! Seule la manière dont vous répondez peut être modifiée, mais cela est plutôt trivial (c'est-à-dire qu'au lieu de transférer au format JSP, imprimez simplement du JSON ou du XML ou même du texte brut, selon le script attendu par l'appeleur responsable de l'appel Ajax).


J'espère que tout ça aide :)

1151
BalusC

Si vous utilisez Spring MVC, voici comment: (je laisse ceci ici au cas où quelqu'un le jugerait utile).

Utiliser un formulaire avec l'attribut enctype défini sur "multipart/form-data" (identique à la réponse de BalusC)

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

Dans votre contrôleur, mappez le paramètre de requête file à MultipartFile tapez comme suit:

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public void handleUpload(@RequestParam("file") MultipartFile file) throws IOException {
    if (!file.isEmpty()) {
            byte[] bytes = file.getBytes(); // alternatively, file.getInputStream();
            // application logic
    }
}

Vous pouvez obtenir le nom du fichier et sa taille en utilisant MultipartFile's getOriginalFilename() et getSize().

J'ai testé cela avec la version Spring 4.1.1.RELEASE.

25
Amila

Vous avez besoin que le fichier common-io.1.4.jar soit inclus dans votre répertoire lib, ou si vous travaillez dans un éditeur, tel que NetBeans, vous devez vous rendre dans les propriétés du projet et simplement ajouter le fichier JAR. sera fait.

Pour obtenir le fichier common.io.jar, il suffit de le rechercher sur Google ou d’accéder simplement au site Web Apache Tomcat , sur lequel vous pouvez télécharger ce fichier gratuitement. Mais souvenez-vous d’une chose: téléchargez le fichier Zip binaire si vous utilisez Windows.

12
Pranav

J'utilise Servlet commun pour tous Formulaire HTML, qu'il ait des pièces jointes ou non. Ce servlet retourne un TreeMap où les clés sont nom_jsp. Les paramètres et les valeurs sont des entrées utilisateur. Il enregistre toutes les pièces jointes dans un répertoire fixe. Vous renommez ensuite le répertoire de votre choix. Here Connections est notre interface personnalisée dotée d'un objet de connexion. Je crois que ceci 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;
    }
}

Pour Spring MVC Cela fait des heures que j'essaye de faire cela et je parviens à avoir une version plus simple qui fonctionne pour la saisie de données à la fois sous forme d'image et d'image.

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

Contrôleur à manipuler

@Controller
public class FormController {
    @RequestMapping(value="/handleform",method= RequestMethod.POST)
    ModelAndView register(@RequestParam String name, @RequestParam int age, @RequestParam MultipartFile file)
            throws ServletException, IOException {

        System.out.println(name);
        System.out.println(age);
        if(!file.isEmpty()){
            byte[] bytes = file.getBytes();
            String filename = file.getOriginalFilename();
            BufferedOutputStream stream =new BufferedOutputStream(new FileOutputStream(new File("D:/" + filename)));
            stream.write(bytes);
            stream.flush();
            stream.close();
        }
        return new ModelAndView("index");
    }
}

J'espère que ça aide :)

8
Shivangi Gupta

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

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

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 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 ou 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 de servlet, telles que , taille de fichier maximale , , taille de demande maximale , ainsi que d'autres options à votre disposition. ensemble...

8
chepe lucho

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

fileItems = uploader.parseRequest(request);

Notez que le type List de fileItems a été modifié avec la version actuelle de commons-fileupload pour devenir spécifiquement List<FileItem> par opposition aux versions précédentes dans lesquelles il était générique List.

J'ai ajouté le code source de commons-fileupload et commons-io dans mon projet Eclipse pour tracer l'erreur réelle et enfin avoir un aperçu. Tout d’abord, l’exception levée est de type Throwable, pas l’exception FileIOException ni même Exception (elles ne seront pas interceptées). Deuxièmement, le message d'erreur est obscurcissant dans le sens où il est indiqué que la classe n'est pas trouvée car 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 référentiel Geronimo dans le cadre d'une installation standard.

Finalement, j'ai trouvé un endroit qui posait une solution qui résolut mon problème avec succès. Vous devez masquer les fichiers JAR du chargeur parent dans le plan de déploiement. Cela 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>
6
Geoffrey Malafsky

Voici un exemple utilisant Apache commons-fileupload:

// Apache commons-fileupload to handle file upload
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(DataSources.TORRENTS_DIR()));
ServletFileUpload fileUpload = new ServletFileUpload(factory);

List<FileItem> items = fileUpload.parseRequest(req.raw());
FileItem item = items.stream()
  .filter(e ->
  "the_upload_name".equals(e.getFieldName()))
  .findFirst().get();
String fileName = item.getName();

item.write(new File(dir, fileName));
log.info(fileName);
0
thouliha