Java e HttpURLConnection: upload di un file

Partiamo dall’RFC 1867, che definisce le modalità con cui eseguire un upload verso una applicazione web.

http://www.ietf.org/rfc/rfc1867.txt

Non è il Signore del Anelli, ma è comunque una lettura interessante (e molto più corta), se vi interessa l’argomento.
Un piccolo sunto:

Nel paragrafo 6 del documento c’è un esempio che spiega quello che faremo poi in java. Questa è una form presente in una pagina HTML:

<FORM ACTION="http://server.dom/cgi/handle"
ENCTYPE="multipart/form-data"
METHOD=POST>
What is your name? <INPUT TYPE=TEXT NAME=submitter>
What files are you sending? <INPUT TYPE=FILE NAME=pics>
</FORM>

A fronte di questa form, in cui viene digitato “Joe Blow” alla domanda “What is your name” e file1.txt alla domanda”What files are you sending”, il client (browser o java nel nostro esempio) deve passare le seguenti informazioni:

Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--AaB03x--

La variabile denominata boundary serve per delimitare il contenuto del file. Deve essere una stringa unica all’interno del MIME. Quando un post viene eseguito da un browser, è lui stesso che si preoccupa di questo.

Se l’utente avesse specificato anche una seconda immagine, ad esempio file2.gif, il cliente avrebbe dovuto comporre:

Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="pics"
Content-type: multipart/mixed, boundary=BbC04y

--BbC04y
Content-disposition: attachment; filename="file1.txt"
Content-Type: text/plain

... contents of file1.txt ...
--BbC04y
Content-disposition: attachment; filename="file2.gif"

Content-type: image/gif
Content-Transfer-Encoding: binary

...contents of file2.gif...
--BbC04y--
--AaB03x--

Vengono usato due boundary, uno interno per delimitare i due file, ed uno esterno per delimitare il contenuto della form.
Quindi, attraverso le stringhe Content-disposition e Content-Type specifichiamo, di volta in volta, che tipo di dati stiamo passando nella chiamata POST, se campi, testo, immagini ecc…

Ecco come inviare, attraverso un client Java e la classe HttpURLConnection un file di testo.  Nel mio esempio l’url chiamato è una servlet:

HttpURLConnection conn = null;
DataOutputStream dos = null;
DataInputStream inStream = null;
String exsistingFileName = "C:\\spinea.txt";
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;

//definisco un boundary
String lineEnd = "\r\n";
String twoHyphens = "--";
String boundary = "*****";

//Url da chiamare
String urlString = "http://localhost:8080/TestUpload/servletupload";

try {

// Apro una connessione alla mia servlet
URL url = new URL(urlString);
// Apro una conessione HTTP
conn = (HttpURLConnection) url.openConnection();
// Imposto alcuni parametri per la connessione
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
//Il Content-Type della form
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

//Apro lo streaming verso la servlet
dos = new DataOutputStream( conn.getOutputStream() );

//Scrivo la prima riga
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"upload\";" + " filename=\"" + exsistingFileName +"\"" + lineEnd);
dos.writeBytes(lineEnd);

//Leggo il file di testo che devo inviare, e lo metto in un buffer.
//Nell'esempio qui sotto, viene definito un maxBufferSize,
//per limitare la grandezza del file che viene inviato.

int maxBufferSize = 1*1024*1024;
FileInputStream fileInputStream = new FileInputStream( new File("/home/nicola/documenti/prova.txt") );

//Creo un buffer con dimensione minima fra quella del file e quella impostata come massima.
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];

// leggo il file e invio i byte alla servlet
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0) {

dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);bytesRead = fileInputStream.read(buffer, 0, bufferSize);

}

//Chiudo il file
fileInputStream.close();
//Invio il boundary per delimitare la fine del file
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

//A questo punto devo rimanere in attesa della risposta della servlet, perchè altrimenti il file non sarà caricato
try {

inStream = new DataInputStream ( conn.getInputStream() );
String str;
while (( str = inStream.readLine()) != null) {

System.out.println("Server response is: "+str);
System.out.println("");

}
inStream.close();

}

catch (IOException ioex) {

System.out.println("From (ServerResponse): "+ioex);

}

}
//Seguono tutti i vari catch
catch (MalformedURLException ex) {

System.out.println("From ServletCom CLIENT REQUEST: " + ex);

}
catch (IOException ioe) {

System.out.println("From ServletCom CLIENT REQUEST:" + ioe);

}
catch (Exception ex) {

System.out.println("Eccezzione generica: " + ex);

}

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>