C�mo desarrollar un file-upload con el m�todo POST
© dr7tbien@wanadoo.es
En este HOWTO voy a explicar la manera de enviar datos al server usando el m�todo POST. Podemos encontar informaci�n adicional en rfc 1867 y http://java.sun.com. Una etiqueta FORM cl�sica podr�a ser:
<form action="http://www.servidor.com/servlets/servletProcesadorDeDatos" method="post">
<input type="text" name="campoTexto">
<input type="submit" value="Enviar datos">
</form>
Todos los formularios usan por defecto un atributo ENCTYPE con el siguiente valor por defecto:
application/x-www-form-urlencoded
Si tratamos de enviar un archivo al server mediante un formulario HTML las etiquetas deben de ser de la siguiente manera. supongamos un formulario como el que sigue, En color rojo la parte que m�s nos interesa:
<form action="http://www.servidor.com/servlets/CapturarArchivo"
enctype="multipart/form-data" method="post">
<input type="text" name="ref">
<input type="text" name="login">
<input type="text" name="passwd">
<input type="file">
<input type="submit" value="Enviar datos">
</form>
Cuando un navegador encuentra el atributo "file" en el campo INPUT, muestra un explorador del disco duro a fin de selecionar un archivo del mismo. Una vez seleccionado y escogido el archivo, el navegaor comienza la emisi�n de datos hacia el servidor.
Este formualrio dispone de dos campos, uno que podr�amos llamar "com�n"
<input type="text" name="campoTexto"> y un campo file que es el que enviar� los archvos al server. Cuando el navegador est� habilitado para enviar datos del tipo multipart/form-data, genera un "limite" llamado en ingl�s boundary. El env�o que se hace al server ser� de la siguiente manera:
linea 1: content-type=multipart/form-data;boundary=--------------------------98347523489752348975234957987
linea 2: --------------------------98347523489752348975234957987
linea 3: Content-Disposition:form-data;name="campoTexto"
linea 4:
linea 5: Un texto cualquiera intorducido en el campo de texto
linea 6: --------------------------98347523489752348975234957987
linea 7: Content-Disposition:form-data;name="elegidor";filename="archivo.jpg"
linea 8: Content-Type: image/jpeg
linea 9:
linea 10:
linea 11: CONTENIDO DEL ARCHIVO
linea 12:
linea 13:
....
linea xx: --------------------------98347523489752348975234957987--
Usualmente los formularios que insertamos en nuestros documentos HTML son capaces de enviar datos al server usando alguno de los siguientes m�todos:
package mipaquete;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* Este servlet, llama a la clase CapturadorDatos,
* y usa los m�todos de dicha clase para capturar los campos del formulario y
* el archivo que se env�a
*
* @author dr7tbien
*/
public class CapturarArchivo extends HttpServlet {
public void init(ServletConfig sc) throws ServletException {
super.init(sc);
}//fin init()
public void destroy() {
super.destroy();
}//fin destroy
public void doPost(HttpServletRequest request
HttpServletResponse response)
throws ServletException, IOException {
String login, passwd, ref;
response.setContentType("text/html");
login = request.getParameter("login");
passwd= request.getParameter("passwd");
PrintWriter out = response.getWriter();
CapturadorArchivos ca = new CapturadorArchivos(out);
/*
*Los par�metrops del m�todo obtenerValores son:
* - Una cadena de caracteres con los nombres de los campos
* del formulario separados por espacios en blanco
* - request
*/
String datos[] = ca.obtenerValores("ref login passwd", request);
// mostramos los campos capturados del formulario que no s�n el archivo
for (int i =0; i<datos.length; i++)
out.println("<p>Dato = "+datos[i]);
//Hacemos que el archivo que vamos a capturar se llame como el nombre del campo 'ref' String path = new String("/usr/local/httpd/uploads/"+datos[0]);
/* Pasamos como par�metros al m�todo
* -- El request
* -- El PrintWriter
* -- El path del fichero a capturar
* -- El tama�o m�ximo (en bytes) del archivo que env�a el cliente
*/
ca.capturarArchivo(request, out, path, 300000);
}
}
La clase CapturadorArchivos es de la siguiente manera:
package tiendas;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
class CapturadorArchivos {
PrintWriter out;
/*
* Constructor, recibe los siguientes par�metros:
*
* String s --> Una cadena de caracteres con los campos del formulario
* separados por espacios en blanco para crear un StringTokenizer
* String path --> El path hasta el directorio donde se guardar�n los archivos recibidos
* PrintWriter po --> El escribidor en la web
*
* IMPORTANTE: Ninguno de los campos del formulario que se env�a al servlet se puede llamar
* 'filename'
*/
public CapturadorArchivos(PrintWriter po) {
out = new PrintWriter(po);
}
public String[] obtenerValores (String s, HttpServletRequest request) {
StringTokenizer campos = new StringTokenizer (s, " ");
String valores[] = new String[campos.countTokens()];
/*
* Comprobamos que el m�todo de petici�n es POST
* y que es una petici�n multipart/form-data
*/
try {
if (!(request.getMethod().equals("POST") &&
request.getContentType().startsWith("multipart/form-data")))
throw new Exception("El m�todo no es POST o multipart/form-Data");
int indexBoundary =request.getContentType().indexOf("boundary=");
if (indexBoundary < 0)
throw new Exception("No se ha encontrado la cadena 'boundary=' en request.getContentType");
String boundary = request.getContentType().substring(indexBoundary+9);
ServletInputStream sis =request.getInputStream();
byte tmpbuffer[] = new byte[8192];
// capturamos la primera linea del envio de datos
int length;
String inputLine, campos;
int numeroDatos = campos.countTokens();
for(int i = 0; i < numeroDatos;i++) {
//linea del boundary
length = sis.readLine(tmpbuffer, 0, tmpbuffer.length);
inputLine=new String(tmpbuffer, 0, 0, length);
if(inputLine.indexOf(boundary) < 0)
throw new Exception("No se ha encontrado el boundary en la iteraci�n"+i);
length = sis.readLine(tmpbuffer, 0, tmpbuffer.length);
inputLine=new String(tmpbuffer, 0, 0, length);
if(inputLine.indexOf("filename") > 0)
return valores;
campo = new String(campos.nextElement().toString());
//Leemos la siguiente linea, que ser� un retorno de carro
length = sis.readLine(tmpbuffer, 0, tmpbuffer.length);
if(length < 0)
throw new Exception("No se encuentra el retorno de carro");
length = sis.readLine(tmpbuffer, 0, tmpbuffer.length);
inputLine=new String(tmpbuffer, 0, 0, length);
inputLine = inputLine.substring(0,inputLine.length()-2);
if(length<0)
throw new Exception("Faltan datos, trasmisi�n defectuosa");
valores[i] = new String(inputLine);
}
}catch (Exception ex) {
out.println("<p>" + ex.toString());
return valores;
}
return valores;
}
public void capturarArchivo(HttpServletRequest request, PrintWriter out,
String path, int tam){
int indexBoundary =request.getContentType().indexOf("boundary=");
try {
if (indexBoundary < 0)
throw new Exception("No se ha encontrado la cadena 'boundary=' en request.getContentType");
}catch (Exception ex) {
out.println("<p>"+ex.toString());
}
String boundary = request.getContentType().substring(indexBoundary+9);
try{
ServletInputStream sis =request.getInputStream();
String inputLine;
int length;
int iteracion=0;
byte tmpbuffer[] = new byte[8192];
while (true){
iteracion++;
out.println("<p>"+iteracion);
if((length = sis.readLine(tmpbuffer, 0, tmpbuffer.length)) < 0)
throw new Exception("No se han recibido los datos correctamente");
inputLine = new String(tmpbuffer, 0, 0, length);
out.println("<p>Linea"+inputLine);
out.println("<p>Longitud linea "+inputLine.length());
//Situamos el puntero en posici�n de lectura del archivo
if(inputLine.indexOf("filename") >= 0) {
if((length = sis.readLine(tmpbuffer, 0, tmpbuffer.length)) < 0)
throw new Exception("No se han recibido los datos correctamente");
inputLine = new String(tmpbuffer, 0, 0, length);
break;
}
}
//Se establece el tama�o m�ximo del archivo
byte fileBytes[] = new byte[tam];
//Leemos la primera linea del archivo
out.println("<p>Linea = "+inputLine);
out.println("<p>Linea TAM = "+inputLine.length());
if (inputLine.indexOf("Content") >= 0){
out.println("<br>linea 17= "+inputLine);
length = sis.readLine(tmpbuffer, 0, tmpbuffer.length);
if(length < 0)
throw new Exception("No se han recibido los datos correctamente");
inputLine = new String(tmpbuffer, 0, 0, length);
}
length = sis.readLine(tmpbuffer, 0, tmpbuffer.length);
if(length < 0)
throw new Exception("No se han recibido los datos correctamente");
inputLine = new String(tmpbuffer, 0, 0, length);
int offset = 0;
while(inputLine.indexOf(boundary) < 0) {
out.println("<p>Linea = "+inputLine);
System.arraycopy(tmpbuffer, 0, fileBytes, offset, length);
offset += length;
length = sis.readLine(tmpbuffer, 0, tmpbuffer.length);
if(length < 0)
throw new Exception("No se han recibido los datos correctamente");
if ( (offset+length)>tam)
throw new Exception("El archivo que usted intenta mandar es m�s grande de lo
permitido.
No se pueden enviar archivos mayores de "+tam+"bytes");
inputLine = new String(tmpbuffer, 0, 0, length);
}
out.println("<p>Linea = "+inputLine);
RandomAccessFile raf = new RandomAccessFile(path, "rw");
raf.write(fileBytes, 0, offset-2);
}catch (Exception ex){
out.println("<p>"+ex.toString());
}
}
}