Upload di un file in Java con le Servlet 3.0

DOMANDA:

Esiste un modo per effettuare l'upload di un file via JSP/Servlet senza utilizzare librerie di terze parti?


RISPOSTA:

Una delle problematiche che più spesso si presenta a chi progetta siti web è l'upload di un file da un form a una JSP/Servlet.

Sino all'avvento delle Servlet 3.0 e all'introduzione dell'annotazione @MultipartConfig era necessario utilizzare le ottime librerie messe a disposizione da O'reilly (COS) o da Apache (FileUpload).

In realtà anche senza librerie di terze parti e Servlet 3.0 era possibile effettuare un upload, ma sicuramente il codice da utilizzare non era dei più puliti.

Ve lo dimostro con un esempio vecchia maniera (senza utilizzo di librerie di terze parti) che, dopo aver letto il file dalla request, lo salva sul disco del server:

Il form .JSP per l'upload del file:
<html>
   <head><title>Test File Upload</title></head>
   <body>

      <form name="fileUpload" method="post" action="/servlet/fileUpload" enctype="multipart/form-data">
         <label>File da inviare al server</label> 
         <input type="file" name="nomeFile" />
      </form>

   </body>
</html>
IMPORTANTE: è necessario specificare enctype="multipart/form-data" per permettere l'invio del file nella Request.


Ecco la corrispondente Servlet ("vecchia maniera"):
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// esempio preso da http://www.roseindia.net/

@WebServlet(name="VecchiaServlet", urlPatterns="/servlet/fileUpload")
public class VecchiaServlet extends HttpServlet {
   private static final long serialVersionUID = 1L;
   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      // ricaviamo il content type
      String contentType = request.getContentType();
      //check se il content type non è nullo e mulitpart/form-data contiene qualcosa
      if ((contentType != null) && (contentType.indexOf("multipart/form-data") >= 0)) {
         DataInputStream in = new DataInputStream(request.getInputStream());

         //lunghezza del contenuto del content type
         int formDataLength = request.getContentLength();
         byte dataBytes[] = new byte[formDataLength];
         int byteRead = 0;
         int totalBytesRead = 0;
         //convertiamo il file in upload in byte code
         while (totalBytesRead < formDataLength) {
            byteRead = in.read(dataBytes, totalBytesRead, formDataLength);
            totalBytesRead += byteRead;
         }

         String file = new String(dataBytes);
         // ricaviamo il nome del file
         String nomeFile = file.substring(file.indexOf("filename=\"") + 10);
         nomeFile = nomeFile.substring(0, nomeFile.indexOf("\n"));
         nomeFile = nomeFile.substring(nomeFile.lastIndexOf("\\") + 1,nomeFile.indexOf("\""));
         int lastIndex = contentType.lastIndexOf("=");
         String boundary = contentType.substring(lastIndex + 1,contentType.length());
         int pos;
         // controlliamo da dove comincia realmente il contenuto del file (senza intestazione)
         pos = file.indexOf("filename=\"");
         pos = file.indexOf("\n", pos) + 1;
         pos = file.indexOf("\n", pos) + 1;
         pos = file.indexOf("\n", pos) + 1;
         int boundaryLocation = file.indexOf(boundary, pos) - 4;
         int posizioneIniziale = ((file.substring(0, pos)).getBytes()).length;
         int posizioneFinale = ((file.substring(0, boundaryLocation)).getBytes()).length;
   
         // creiamo un nuovo file con lo stesso nome e contenuto del file in upload
         FileOutputStream fileOut = new FileOutputStream(nomeFile);
         fileOut.write(dataBytes, posizioneIniziale, (posizioneFinale - posizioneIniziale));
         fileOut.flush();
         fileOut.close();

         System.out.println("File "+nomeFile+" inviato con succeso");
      }
   }
}

Guardiamo invece come risparmiare numerose righe di codice grazie all'avvento delle Servlet 3.0:
package fileupload;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

/**
 * Servlet implementation class FileUpload
 * @author http://lancill.blogspot.it
 * @version 1.0
 */
@WebServlet(name="FileUpload", urlPatterns="/servlet/fileUpload")
@MultipartConfig
public class FileUpload extends HttpServlet {
   private static final long serialVersionUID = 1L;
   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

      Part filePart = request.getPart("nomeFile");

      //String nomeFile = getFilename(filePart);

      BufferedReader reader = new BufferedReader(new InputStreamReader(filePart.getInputStream()));
      String line = null;
      while ((line = reader.readLine()) != null)
         System.out.println(line);
   }
}
Notiamo l'annotation @MultipartConfig, senza non saremmo stati capaci di ricevere il file in upload. 

Nell'ultimo esempio ci limitiamo solo alla lettura e la scrittura del contenuto del file su console, il passo per salvarlo fisicamente sull'hd del server può essere ricavato dall'esempio iniziale.

Se volessimo avere a disposizione il nome del file in upload dobbiamo aggiungere un metodo non troppo intuitivo (ma nemmeno molto complesso) e, ovviamente, decommentare la riga numero 28 del precedente esempio:
private static String getFilename(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;
}
Come vediamo la Servlet si semplifica parecchio, quasi come con l'utilizzo delle librerie di terze parti.

Vi ricordo che per utilizzare le Servlet 3.0 è necessario Tomcat 7 (o equivalenti) e il relativo servlet-api.jar aggiornato all'ultima release.


Commenti

  1. Ciao,
    trovo molto interessante il tuo articolo, tuttavia mi perdo nel trasferimento fisico del file al server.
    Potresti scrivere qualche esempio?
    Grazie

    RispondiElimina
    Risposte
    1. Ciao, scusa il ritardo. Non hai dettagliato molto il problema, però provo lo stesso a risponderti:
      1 - Nel form .jsp hai dato lo stesso nome (l'attributo input name) sia al tag HTML sia quando prendi il file con request.getPart?
      2- Hai inserito l'annotazione @MultipartConfig?

      Se ancora non hai risolto, dovresti darmi qualche altro dettaglio!

      Elimina

Posta un commento

Post popolari in questo blog

Arrotondamento e troncamento in Java

Eclipse: Shortcuts (scorciatoie) da tastiera

Strutture dati: List, Set, Map

Creare un eseguibile Java