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