2010-05-13 54 views
19

Estoy trabajando en una aplicación web java en la que los archivos se almacenarán en una base de datos. Originalmente se recuperaron los archivos que ya están en la base de datos, simplemente llamando a getBytes en nuestro conjunto de resultados:Cómo convertir un InputStream a un DataHandler?

byte[] bytes = resultSet.getBytes(1); 
... 

Esta matriz de bytes se convierte entonces en un DataHandler utilizando el constructor obvia:

dataHandler=new DataHandler(bytes,"application/octet-stream"); 

Esto funcionó muy bien hasta que nos comenzó a tratar de almacenar y recuperar archivos más grandes. Volcar todo el contenido del archivo en una matriz de bytes y luego crear un DataHandler simplemente requiere demasiada memoria.

Mi idea inmediata es recuperar una secuencia de los datos en la base de datos con getBinaryStream y de alguna manera convertir ese InputStream en un de una manera eficiente con la memoria. Desafortunadamente, no parece que haya una forma directa de convertir un InputStream en un DataHandler. Otra idea con la que he estado jugando es leer trozos de datos del InputStream y escribirlos en el OutputStream del DataHandler. Pero ... No puedo encontrar una forma de crear un DataHandler "vacío" que devuelva un OutputStream no nulo cuando llamo a getOutputStream ...

¿Alguien ha hecho esto? Agradecería cualquier ayuda que pueda darme o me guiará en la dirección correcta.

Respuesta

14

Mi enfoque sería escribir una clase personalizada implementando DataSource que envuelve su InputStream. Luego crea el DataHandler dándole el DataSource creado.

+0

Ah, que es una gran idea. Lo intentaré cuando tenga oportunidad. – pcorey

+0

Pensé lo mismo. Pero ten cuidado, entonces el DataHandler debe ser usado (consume su entrada), "dentro de ti loop", mientras que el ResultSet está abierto. Por ejemplo, probablemente no puede pasar el objeto DataHandler a una capa superior. – leonbloy

+0

@leonbloy El objetivo declarado era procesar los datos sin copiarlos del conjunto de resultados. Esto implica que el conjunto de resultados debe estar abierto todo el tiempo, independientemente de cómo lo haga. –

16

También me encontré con este problema. Si sus datos de origen son byte[], Axis ya tiene una clase que envuelve InputStream y crea un objeto DataHandler. Aquí está el código

//this constructor takes byte[] as input 
ByteArrayDataSource rawData= new ByteArrayDataSource(resultSet.getBytes(1)); 
DataHandler data= new DataHandler(rawData); 
yourObject.setData(data); 

importaciones relacionadas

import javax.activation.DataHandler; 
import org.apache.axiom.attachments.ByteArrayDataSource; 

espero que ayude!

+3

Como carga todos los datos en la memoria, podría causar problemas al administrar datos de gran tamaño. –

3

Tenga en cuenta que el getInputStream de DataSource debe devolver un nuevo InputStream cada vez que se llame. Esto significa que debe copiar en algún lugar primero. Para obtener más información, consulte http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4267294

+0

Sé que es viejo ... ¿es ese error real? – Cris

+2

La API dice eso. Sin embargo, dice devolver una nueva transmisión o lanzar una excepción. Técnicamente, eso significa devolver una secuencia la primera vez y luego lanzar excepciones. Supongo que la mayoría de los marcos solo recuperan la transmisión una vez. – Steve11235

14

Una implementación de respuesta de "Kathy Van Stone":

En primer lugar crear clase de ayuda, que crean origen de datos de InputStream:

public class InputStreamDataSource implements DataSource { 
    private InputStream inputStream; 

    public InputStreamDataSource(InputStream inputStream) { 
     this.inputStream = inputStream; 
    } 

    @Override 
    public InputStream getInputStream() throws IOException { 
     return inputStream; 
    } 

    @Override 
    public OutputStream getOutputStream() throws IOException { 
     throw new UnsupportedOperationException("Not implemented"); 
    } 

    @Override 
    public String getContentType() { 
     return "*/*"; 
    } 

    @Override 
    public String getName() { 
     return "InputStreamDataSource"; 
    } 
} 

y luego se puede crear DataHandler de InputStream:

DataHandler dataHandler = new DataHandler(new InputStreamDataSource(inputStream)) 

importaciones:

import javax.activation.DataSource; 
import java.io.OutputStream; 
import java.io.InputStream; 
+0

'getInputStream' debería devolver un nuevo' InputStream' cada vez que se llama al – husayt

+0

¿Podría explicar el motivo para esto, por favor? – Gordak

0

(bugs_) código no funciona para mí.Utilizo DataSource para crear Adjuntos al correo electrónico (desde objetos que tienen inputStream y nombre) y el contenido de los archivos adjuntos perdidos. Parece que Stefan tiene razón y se debe devolver un nuevo inputStream cada vez. Al menos en mi caso específico. Próximas ofertas de implementación con un problema:

public class InputStreamDataSource implements DataSource { 

    ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 
    private final String name; 

    public InputStreamDataSource(InputStream inputStream, String name) { 
     this.name = name; 
     try { 
      int nRead; 
      byte[] data = new byte[16384]; 
      while ((nRead = inputStream.read(data, 0, data.length)) != -1) { 
       buffer.write(data, 0, nRead); 
      } 

      buffer.flush(); 
      inputStream.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 

    @Override 
    public String getContentType() { 
     return new MimetypesFileTypeMap().getContentType(name); 
    } 

    @Override 
    public InputStream getInputStream() throws IOException { 
     return new ByteArrayInputStream(buffer.toByteArray()); 
    } 

    @Override 
    public String getName() { 
     return name; 
    } 

    @Override 
    public OutputStream getOutputStream() throws IOException { 
     throw new IOException("Read-only data"); 
    } 

} 
0

He reúnen situación, cuando InputStream solicitarse a DataSource dos veces: el uso de Manipulador de registro junto con la función de masa máxima de despegue. Con this proxy stream solution mi aplicación funciona bien:

import org.apache.commons.io.input.CloseShieldInputStream; 
import javax.activation.DataHandler; 
import javax.activation.DataSource; 
... 

private static class InputStreamDataSource implements DataSource { 
    private InputStream inputStream; 

    @Override 
    public InputStream getInputStream() throws IOException { 
     return new CloseShieldInputStream(inputStream); 
    } 

    @Override 
    public OutputStream getOutputStream() throws IOException { 
     throw new UnsupportedOperationException("Not implemented"); 
    } 

    @Override 
    public String getContentType() { 
     return "application/octet-stream"; 
    } 

    @Override 
    public String getName() { 
     return ""; 
    } 
} 
0

Aquí es una respuesta para el trabajo específico con el objeto de Primavera de arranque org.springframework.core.io.Resource que es pienso en lo que muchos de nosotros están consiguiendo aquí. Tenga en cuenta que es posible que necesite modificar el tipo de contenido en el siguiente código ya que estoy insertando un archivo png en un correo electrónico con formato html.

Nota: Como otros lo han mencionado, simplemente adjuntar un InputStream no es suficiente ya que se usa varias veces, solo hacer un mapeo a Resource.getInputStream() hace el truco.

public class SpringResourceDataSource implements DataSource { 
    private Resource resource; 

    public SpringResourceDataSource(Resource resource) { 
     this.resource = resource; 
    } 

    @Override 
    public InputStream getInputStream() throws IOException { 
     return resource.getInputStream(); 
    } 

    @Override 
    public OutputStream getOutputStream() throws IOException { 
     throw new UnsupportedOperationException("Not implemented"); 
    } 

    @Override 
    public String getContentType() { 
     return "image/png"; 
    } 

    @Override 
    public String getName() { 
     return "SpringResourceDataSource"; 
    } 
} 

Uso de la clase es el siguiente:

PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver(); 
    Resource logoImage = pathMatchingResourcePatternResolver.getResource("/static/images/logo.png"); 
    MimeBodyPart logoBodyPart = new MimeBodyPart(); 
    DataSource logoFileDataSource = new SpringResourceDataSource(logoImage); 


    logoBodyPart.setDataHandler(new DataHandler(logoFileDataSource)); 
Cuestiones relacionadas