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
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 !
0 votes
Vérifiez ceci sandny.com/2017/05/18/servlet-file-upload