2010-05-13 41 views
19

Sto lavorando a un'applicazione web java in cui i file verranno archiviati in un database. Originariamente abbiamo recuperato i file già nel DB semplicemente chiamando getBytes sul nostro set di risultati:Come convertire un InputStream in un DataHandler?

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

Questo array di byte è stato poi convertito in un DataHandler utilizzando il costruttore ovvia:

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

Questo ha funzionato bene fino a quando abbiamo ha iniziato a provare a memorizzare e recuperare file più grandi. Scaricando l'intero contenuto del file in un array di byte e creando quindi un DataHandler, è sufficiente disporre di troppa memoria.

La mia idea immediata è di recuperare un flusso di dati nel database con getBinaryStream e in qualche modo convertire tale InputStream in un DataHandler in modo efficiente dalla memoria. Sfortunatamente non sembra che ci sia un modo diretto per convertire un InputStream in un DataHandler. Un'altra idea con cui ho giocato è la lettura di blocchi di dati dallo InputStream e la loro scrittura nello OutputStream dello DataHandler. Ma ... Non riesco a trovare un modo per creare un "vuoto" DataHandler che restituisce un valore non nullo OutputStream quando chiamo getOutputStream ...

Qualcuno ha fatto questo? Apprezzerei qualsiasi aiuto tu possa darmi o guidi nella giusta direzione.

risposta

14

Il mio approccio sarebbe quello di scrivere una classe personalizzata implementando DataSource che avvolge il numero InputStream. Quindi creare il DataHandler dandogli il DataSource creato.

+0

Ah, che è una grande idea. Ci proverò quando avrò la possibilità. – pcorey

+0

Ho pensato la stessa cosa. Ma attenzione, allora il DataHandler deve essere usato (consuma il suo input), "inside you loop", mentre il ResultSet è aperto. Ad esempio, non puoi probabilmente passare l'oggetto DataHandler a un livello superiore. – leonbloy

+0

@leonbloy L'obiettivo dichiarato era elaborare i dati senza copiarli dal set di risultati. Ciò implica che il set di risultati deve essere aperto per tutto il tempo indipendentemente da come lo si fa. –

16

Mi sono anche imbattuto in questo problema. Se i tuoi dati di origine sono un byte[] Axis ha già una classe che avvolge InputStream e crea un oggetto DataHandler. Ecco il codice

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

importazioni correlati

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

Speranza che aiuta!

+3

Poiché carica tutti i dati nella memoria, causerebbe problemi nella gestione di dati di grandi dimensioni. –

3

Si noti che getInputStream di DataSource deve restituire un nuovo InputStream ogni volta chiamato. Questo significa che devi copiare da qualche parte il primo. Per ulteriori informazioni, vedere http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4267294

+0

So che è vecchio ... questo bug è reale? – Cris

+2

L'API dice questo. Tuttavia, dice restituire un nuovo stream o generare un'eccezione. Tecnicamente, ciò significa restituire un flusso per la prima volta e quindi generare eccezioni. Presumo che la maggior parte dei framework recuperi il flusso una sola volta. – Steve11235

14

Un'implementazione di risposta da "Kathy Van Stone":

All'inizio creare classe di supporto, che creano DataSource da 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"; 
    } 
} 

e poi si può creare DataHandler da InputStream:

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

importazioni:

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

'getInputStream' dovrebbe restituire un nuovo' InputStream' ogni volta quando viene chiamato – husayt

+0

Puoi spiegare il motivo a questo per favore? – Gordak

0

(bugs_) codice non fa per me funziona.Utilizzo DataSource per creare allegati a posta elettronica (da oggetti che hanno inputStream e nome) e il contenuto di allegati persi. Sembra che Stefan abbia ragione e il nuovo inputStream deve essere restituito ogni volta. Almeno nel mio caso specifico. Successivo offerte attuazione con 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

Sono situazione si incontrano, quando InputStream richiesto da DataSource due volte: con registrazione Handler insieme con la caratteristica MTOM. Con this proxy stream solution mia implementazione funziona bene:

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

Ecco una risposta per specificamente lavorare con l'oggetto primavera Boot org.springframework.core.io.Resource che è penso a quanto molti di noi sono sempre qui. Si noti che potrebbe essere necessario modificare il tipo di contenuto nel codice sottostante poiché sto inserendo un file png in un'e-mail formattata in html.

Nota: come altri hanno già accennato, il semplice inserimento di un InputStream non è sufficiente in quanto viene utilizzato più volte, ma solo il mapping con Resource.getInputStream() fa il trucco.

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 della classe si presenta così:

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)); 
Problemi correlati