Java EE – Faire une Servlet d'upload

Certaines librairies de composants JSF comme Primefaces proposent des solutions faciles à implémenter pour réaliser vos uploads. En revanche, mettre en oeuvre cette opération soi-même peut sembler plus délicat. Mais nous allons voir que, comme souvent en
Florent TRIPIERMis à jour le 30 Avr 2014
java-logo.jpg

Certaines librairies de composants JSF comme Primefaces proposent des solutions faciles à implémenter pour réaliser vos uploads. En revanche, mettre en oeuvre cette opération soi-même peut sembler plus délicat. Mais nous allons voir que, comme souvent en JavaLangage de développement très populaire !, le standard JEEJava Entreprise Edition définit déjà tous les outils nécessaires.

Tout dabord, il faut déclarer une Servlet et lannoter avec @MultipartConfig. Cette annotation va permettre de prendre en charge les formulaires encodés en multipart.

@WebServlet(name = "UploadServlet", urlPatterns = { "/single-upload" })
@MultipartConfig
public class UploadServlet extends HttpServlet {

Ensuite, la classe javax.servlet.http.Part permet de récupérer un flux à parti duquel vous pouvez enregistrer votre fichier. Lexemple type :

final Collection<
part> lParts = pRequest.getParts();
final PrintWriter lWriter = pResponse.getWriter();
String lFileName = null;
for (Part lFilePart : lParts) {
 lFileName = getFileName(lFilePart);
 // Try-with-resources statement
 try (OutputStream lOutputStream = new FileOutputStream(new File("VOTRE_PATH_UPLOAD" + lFileName));
      InputStream lInputStream = lFilePart.getInputStream()) {
    int lRead = 0;
    final byte[] lBytes = new byte[1024];
    while ((lRead = lInputStream.read(lBytes)) != -1) {
      lOutputStream.write(lBytes, 0, lRead);
    }
    lWriter.println("File has been successfully uploaded.");
  } catch (FileNotFoundException e) {
    lWriter.println("An error occurred while trying to upload file.");
  }
}
if (lWriter != null) {
  lWriter.close();
}
<
/part>

Ici, je récupère une Collection contenant lensemble des Parts de mon HttpServletRequest, et jitère dessus pour gérer les fichiers uploadés. Après, il ne sagit que de lecture et écriture de fichiers : on récupère un flux entrant depuis le Part, un flux sortant vers le fichier dans lequel on souhaite écrire. Le PrintWriter est utilisé pour écrire la réponse HTTP.

Remarques :

  • ici les flux sont déclarés et instanciés entre parenthèses entre le mot-clé try et laccolade ouvrante : si vous ne connaissez pas cette syntaxe, il sagit du try-with-resources statement, nouveauté de Java 7.
  • à quel moment le fichier est-il réellement uploadé ? Lorsque vous appelez la méthode getParts() : le flux ouvert par le formulaire en multipart permet alors lupload depuis le poste client vers un répertoire temporaire de votre serveur dapplication. LInputStream qui est déclaré après pointe donc vers ce fichier temporaire, et lopération de lecture écriture dans le try / catch ne fait que copier localement le fichier uploadé depuis sont répertoire temporaire vers le fichier cible que vous avez défini.
  • la méthode getParts() ramène une Collection de Part. Il est possible de cibler précisément un Part par son nom : il correspond alors à lattribut id de linput de type form depuis lequel est réalisé lupload côté client. Exemple : Part lPart = pRequest.getPart(« file »);
  • ce code ne permet pas de faire des uploads multiples. Le standard Java EE ne définit pas encore les outils adéquats pour cela, mais Apache notamment propose des solutions de qualité et faciles à implémenter (voir les librairies Apache commons).
  • le code ci-dessus fait appel à une méthode getFileName(Part) : en effet, un parsing des métadonnées du Part est nécessaire pour récupérer le nom du fichier source. Exemple ci-dessous :
private String getFileName(final Part pPart) {
  for (String lContent : pPart.getHeader("content-disposition")
      .split(";")) {
    if (lContent.trim().startsWith("filename")) {
      return lContent.substring(lContent.indexOf(’=’) + 1).trim().replace("\"", "");
    }
  }
  return null;
}