6

Estoy subiendo imágenes al servidor. La imagen se la envío en un input file a mi servidor local, la convierto en un arreglo de bytes, seteo el arreglo en un una clase modelo (BasicFileAction) y luego con un objeto de tipo URL hago una conexión hacia un servicio en un servidor donde guardaré la imagen y le envío como parámetro el objeto de tipo BasicFileAction y ya se guarda.

El problema está en que cuando cargo una imagen de más de 3.68MB la imagen no se sube y no me lanza ningun mensaje de error, no entra al catch ni nada parecido. Parece que truena en

 ObjectOutputStream.writeObject(objaBasicFileAction);

Intenté subiendo muchas imágenes, y TODA imagen menor a 3.68MB sí la sube; probé con imágenes mayores a 3.81MB y ya no las sube. Imágenes entre 3.68MB y 3.81MB no pude obtener, por lo que no sé cuál es exactamente el tamaño que sí sube y el que, apartir de él, no.

Mi clase Modelo:

package com.mypackage.actions;

import java.io.Serializable;

public abstract class BasicFileAction implements Serializable{
    private int[] objcFileBytes;
    private String scFileName;

    public int[]  getFileBytes(){
        return objcFileBytes;
    }   

    public void setFileBytes( int[] objaFileBytes ){
        objcFileBytes = objaFileBytes;
    }   

    public String getFileName(){
        return scFileName;
    }   

    public void setFileName(String saFileName){
        scFileName = saFileName ;
    }   
    public abstract Object execute() throws Exception;

}

Esta es el método que hace el request:

public Object executeService(BasicFileAction objaBasicFileAction)
        throws Exception {
        URL objlURLServer = null;
        ObjectInputStream objlResponse = null;
        Object objlObjectResult = null;
        URLConnection objlURLConnection = null;
        ObjectOutputStream objlRequest = null;
        TunnelException exclTunnelException = null;

        try {
            objlURLServer = new URL((String) objcProperties.get(
                        FileRemoteHandler.SERVICE_NAME));
        } catch (MalformedURLException e) {
            throw new Exception(
                "No se puede efectuar la conexion al servidor de fotografias '" +
                (String) objcProperties.get(FileRemoteHandler.SERVICE_NAME) +
                "' : " + e.getMessage());
        }

        try {
            objlURLConnection = objlURLServer.openConnection();
            objlURLConnection.setDoOutput(true);
            objlURLConnection.setUseCaches(false);
            objlURLConnection.setRequestProperty("Content-Type",
                "application/octet-stream");
            System.out.println("terminando1");
            objlRequest = new ObjectOutputStream(new BufferedOutputStream(
                        objlURLConnection.getOutputStream()));

            try{
                objlRequest.writeObject(objaBasicFileAction); //De aquí no pasa con las imágenes mayores a 3.68MB

                objlRequest.flush();
                objlRequest.close();
            }catch(NotSerializableException r){
                System.out.println("1 "+r.getMessage());
            } catch(InvalidClassException t){
                System.out.println("2 "+t.getMessage());
            } catch(IOException w){
                System.out.println("3 "+w.getMessage());
            }

            // get the result input stream
            objlResponse = new ObjectInputStream(new BufferedInputStream(
                        objlURLConnection.getInputStream()));

            // read response back from the server
            objlObjectResult = objlResponse.readObject();

            if (objlObjectResult instanceof TunnelException) {
                exclTunnelException = (TunnelException) objlObjectResult;
                throw new Exception(exclTunnelException.getMessage());
            }

        } catch (Exception exclException) {
            System.out.println("here: "+exclException.getMessage());
            throw new Exception("Error al Ejecutar la Peticion : " +
                exclException.getMessage());
        }

        return objlObjectResult;
    }

Adicionalmente les dejo información de los arreglos de bytes de las diferentes peticiones que hice.

Las que sí se subieron, el tamaño del arreglo está entre paréntesis:

3.68MB (3867314) 
3.19MB (3355578) 
3.05MB (3204054) 

Las que no se subieron:

 4.09MB (4293306)
 4.36MB (4572533)
 3.81MB (3997079)

He revisado el código en el servidor de imágenes, es un servlet. Puse un System.out.println al inicio del servlet para ver si se llegaba a ejecutar el servlet y pues me he dado cuenta que no. No pasa del writeObject pero tampoco llega al servlet.

Alguna idea?

EDIT: He actualizado la manera en la que envío los datos. Imaginando que el arreglo que le enviaba era demasiado grande lo partí en dos. Ahora la clase BasicFileAction tiene un parámetro más: la segunda parte del arreglo. He programado todo para que writeObject reciba un objeto de tipo BasicFileAction, el cuál, como dije antes, tiene un arreglo más que es el la segunda mitad del arreglo de bytes de la imagen. Probé el código y funciona correctamente DE IGUAL FORMA con imágenes que pesan 3.68MB o menos. De igual manera no pasa de ObjectOutputStream.writeObject(objaBasicFileAction); Y en el servidor de imágenes no imprime nada pues no se ejecuta ni la primera línea del servlet.

Con lo que he hecho me he dado cuenta que el problema es la cantidad de información que se le manda al writeObject de java.io.ObjectOutputStream, aunque le pase el arreglo de bytes en muchos arreglos de tamaños menores va a seguir tronando.

¿Qué pasa con writeObject? ¿Es demasiado grande la información que no la puede enviar? ¿Hay alguna manera diferente de enviarle las imágenes?

user2930137
  • 1,351
  • 14
  • 30
  • ¿Y en el servidor qué código hay? – SJuan76 Aug 18 '17 at 14:45
  • Buena pregunta, no sé qué código tiene el servidor. writeObject ya está tocando al servidor o no? Porque objlRequest.flush(); y objlRequest.close(); no se ejecutan. – user2930137 Aug 18 '17 at 14:47
  • `writeObject` escribirá los datos serializados en el *stream* que le has indicado, por lo que ya empezará a enviar los datos. De hecho, desde que haces el `openConnection` ya hay una comunicación con el servidor. – SJuan76 Aug 18 '17 at 14:49
  • `flush` lo que hace es asegurarse de que todo lo que esté en la caché se procese, pero no espera a que llames a `flush` para empezar a enviar datos. – SJuan76 Aug 18 '17 at 14:52
  • O sea que si en el servidor hay una validación la cuál no permita cierto tamaño desde envío información mediante el writeObject este tronará? – user2930137 Aug 18 '17 at 14:53
  • Los servidores tienen configuraciones del tamaño máximo de los archivos que puedes subir. ¿Que servidor de aplicaciones estas usando? Busca esa configuración. También te recomiendo leer esto: https://es.stackoverflow.com/questions/38177/cu%C3%A1l-es-la-manera-correcta-de-guardar-una-imagen-en-mysql-con-android/38204#38204 – abrahamhs Aug 18 '17 at 14:57
  • Podría ser, pero en ese caso lo más normal sería que te lanzara una excepción, no que se quedara colgado. – SJuan76 Aug 18 '17 at 14:58
  • Buscaré sobre eso, @abrahamhs, gracias – user2930137 Aug 18 '17 at 14:59
  • @SJuan76 no es que se quede colgado, la ejecución se termina ahí. Lo hago con Ajax desde el cliente y pues me lanza el onerror. Pero es cierto, es raro que no me caiga en el catch, veré si me pueden dar acceso al servicio ese gracias – user2930137 Aug 18 '17 at 15:01
  • Chicos, el servidor que consumo es un servlet Y revisé el web.xml y no tiene nada de especial, dónde pondrías las configuraciones en un servlet? – user2930137 Aug 18 '17 at 15:50
  • He actualizado el post. – user2930137 Aug 18 '17 at 16:41
  • Si hay una forma utilizando los Streams, con el response.getOutputStream y request.getInputStream – Ajeno Aug 21 '17 at 16:04
  • Podrías mostrar un ejemplo, por favor. – user2930137 Aug 21 '17 at 16:05
  • Que servidor de aplicaciones estas usando?? – abrahamhs Aug 25 '17 at 01:02
  • El servidor tiene el tomcat en windows server 2000 – user2930137 Aug 25 '17 at 13:48
  • Es claramente una configuración del servidor. Personalmente escribiría tests unitarios para no buscar explicaciones esotéricas, como que `writeObject()` tiene algún tipo de límite :-) – Leonardo Herrera Aug 25 '17 at 15:50

2 Answers2

2

Verifica las siguientes configuraciones en tomcat: En $TOMCAT_HOME/conf/server.xml Existe el tag <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/> cambialo por <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxPostSize="67589953" />. Es decir solo agregale maxPostSize="67589953" es el tamaño maximo de datos que se puede enviar por post. Puedes intentar aumentar el tamaño y el timeout.

También ve a la siguiente ruta $TOMCAT_HOME/webapps/manager/WEB-INF/web.xml y ahí busca esta sección y prueba aumentando los valores:

<multipart-config> <!-- 52MB max --> <max-file-size>52428800</max-file-size> <max-request-size>52428800</max-request-size> <file-size-threshold>0</file-size-threshold> </multipart-config>

Es el tamaño máximo para subir archivos. Si dices que no llega al servlet donde guardaras los archivos, el problema debe estar donde subes los archivos. Pero aun así te recomendaría revisar esas configuraciones en el servidor de aplicaciones de destino aparte de el de origen.

Otra cosa importante, revisa los permisos de windows de las carpetas donde se guardaran los archivos, de tal manera que system, tu usuario de windows (se ha de red o no), y otros usuarios que tengan que ver tengan permisos. No soy muy experto en esta parte, pero te recomiendo preguntarle a tu administrador de red.

abrahamhs
  • 3,376
  • 1
  • 16
  • 39
  • Hola, @abrahamhs, ya agregué el atributo al primer archivo que mencionas. Quise agregar lo de multipart-config pero no encuentro la ruta que especificas. Dentro de webapps del tomcat del servidor no hay una carpeta llamada "manager", solo hay una llamada "fileService" (que así se llama mi app, otra "ROOT", "tomcact-docs", "webdavs", y archivos llamados "admin.xml" y "manager.xml" Entré a la carpeta "fileService" y dentro de ella spi encontré el web-inf -> web.xml y agregué el multipart-config justo debajo de como dice (contignuo abajo) – user2930137 Aug 25 '17 at 17:44
  • https://stackoverflow.com/questions/19145489/multipartconfig-override-in-web-xml pero al guardar y levantar el servidor me marcaba un error en el cmd que decía GRAVE: Parse Error at line 13 clumn 21: Element type "max-request-size" must be declared. (Arriba de ese había más errores pero ya no se alcanzó a visualizar en el cmd pues supongo que era demasiado grande el error y se tragó el cmd las primeras líneas. – user2930137 Aug 25 '17 at 17:47
  • Según http://docs.oracle.com/javaee/6/tutorial/doc/gmhal.html los valores default de esa configuración son "sin límites", así que si no se los seteó debería funcionar y no funciona u_u – user2930137 Aug 25 '17 at 18:03
  • Pues reversa todo lo que hiciste hasta que levante y prueba los cambios 1 por 1. Pero se me hace muy extraño que no encuentres la carpeta webapps. Todos los tomcat la tienen. Tal vez ese sea el origen de tus problemas. Las paginas que pones modifican las configuraciones dentro de tu aplicación, por eso la importancia de `$TOMCAT_HOME/webapps/manager/WEB-INF/web.xml` porque ahi se afecta a todo el tomcat. Yo obtuve los datos que te pase de aqui: https://stackoverflow.com/questions/2947683/httprequest-maximum-allowable-size-in-tomcat – abrahamhs Aug 25 '17 at 23:20
  • Hola, revertí los cambios y ya está corriendo como normalmente lo hacía. La carpeta webapps sí está, la que no está es la carpeta "manager" dentro de webapps. – user2930137 Aug 28 '17 at 13:46
  • La recompensa termina en dos horas. Para no perderla te la daré, parece que con las configuraciones esas debería poder correr normalmente, pero por alguna razón no lo hace. Gracias igual – user2930137 Aug 28 '17 at 13:47
1

Algo así:

OutputStream os = new ByteArrayOutputStream(); //Puede ser cualquier OutputStream.
ServletInputStream is       = request.getInputStream();
byte[] bufferData = new byte[1024];
int read=0;
while((read = is.read(bufferData))!= -1){
    os.write(bufferData, 0, read);
}
os.flush();
os.close();
is.close();
Ajeno
  • 1,073
  • 7
  • 14
  • Esto en dónde va? es para hacer el request? – user2930137 Aug 21 '17 at 16:21
  • en que método del Servlet mandas llamar executeService en el doPost? – Ajeno Aug 21 '17 at 16:45
  • Actualmente lo hace en un método llamado processRequest, que es el método que usan doPost y doGet, o sea que ambos llaman a ese, para que la petición pueda ser respondida por ambos request. – user2930137 Aug 21 '17 at 16:59
  • Si ya cambié el método me confundí con los Streams, pero el request es el HttpServletRequest que recibe de parámetro tanto el doPost como el doGet – Ajeno Aug 21 '17 at 17:05
  • O sea que ese que escribes es código del servlet. Ese no es el problema, el problema está en la petición, no en el procesamiento en el servlet. writeObject no escribe la info para mandarla al servlet, ese es el problema. – user2930137 Aug 21 '17 at 17:07