2010-05-13 16 views
19

Ich arbeite an einer Java-Webanwendung, in der Dateien in einer Datenbank gespeichert werden. Ursprünglich abgerufen wir Dateien bereits in der DB durch einfache getBytes auf unserer Ergebnismenge Aufruf:Wie konvertiert man einen InputStream in einen DataHandler?

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

Dieser Byte-Array dann ein in DataHandler umgewandelt wurde, das Offensichtliche Konstruktor:

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

Das funktionierte großartig, bis wir begann zu versuchen, größere Dateien zu speichern und abzurufen. Den gesamten Dateiinhalt in ein Byte-Array zu packen und dann daraus einen DataHandler zu bauen, benötigt einfach zu viel Speicher.

Meine unmittelbare Idee ist es, einen Strom der Daten in der Datenbank mit getBinaryStream abrufen und irgendwie konvertieren InputStream in eine DataHandler auf eine speichereffiziente Weise. Leider scheint es keinen direkten Weg zu geben, eine InputStream in eine DataHandler umzuwandeln. Eine andere Idee, mit der ich gespielt habe, ist das Lesen von Datenbrocken aus der InputStream und das Schreiben dieser Daten in die OutputStream der DataHandler. Aber ... Ich kann keinen Weg finden, eine "leere" DataHandler zu erstellen, die eine nicht null OutputStream zurückgibt, wenn ich getOutputStream anrufen ...

Hat jemand dies getan? Ich würde mich über jede Hilfe freuen, die Sie mir geben oder in die richtige Richtung führen können.

Antwort

14

Mein Ansatz wäre, eine benutzerdefinierte Klasse zu schreiben, die DataSource implementiert, die Ihre InputStream umschließt. Dann erstellen Sie die DataHandler geben Sie die erstellte DataSource.

+0

Ah, das ist eine großartige Idee. Ich werde es versuchen, wenn ich eine Chance bekomme. – pcorey

+0

Ich dachte das Gleiche. Aber Vorsicht, dass dann der DataHandler verwendet werden muss (konsumiere seine Eingabe), "inside you loop", während das ResultSet geöffnet ist. Beispielsweise können Sie das DataHandler-Objekt wahrscheinlich nicht an eine höhere Ebene übergeben. – leonbloy

+0

@leonbloy Das erklärte Ziel war, die Daten zu verarbeiten, ohne sie aus der Ergebnismenge zu kopieren. Dies bedeutet, dass die Ergebnismenge die ganze Zeit geöffnet sein muss, unabhängig davon, wie Sie es tun. –

16

Ich stieß auch auf dieses Problem. Wenn Ihre Quelldaten eine byte[] Axis ist, hat bereits eine Klasse, die den InputStream umschließt und ein DataHandler-Objekt erstellt. Hier ist der Code

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

Verwandte Einfuhren

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

Hoffe, es hilft!

+3

Da alle Daten in den Speicher geladen werden, führt dies zu Problemen bei der Verwaltung großer Datenmengen. –

3

Beachten Sie, dass der getInputStream der DataSource bei jedem Aufruf einen neuen InputStream zurückgeben muss. Dies bedeutet, dass Sie irgendwo 1st kopieren müssen. Weitere Informationen finden Sie http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4267294

+0

Ich weiß, es ist alt ... ist dieser Bug tatsächlich? – Cris

+2

Die API sagt das. Es wird jedoch angezeigt, einen neuen Stream zurückzugeben oder eine Ausnahme auszulösen. Technisch bedeutet das, dass ein Stream das erste Mal zurückgegeben wird und dann Ausnahmen ausgelöst werden. Ich gehe davon aus, dass die meisten Frameworks den Stream nur einmal abrufen. – Steve11235

14

Eine Implementierung der Antwort von "Kathy Van Stone":

Auf den ersten Helfer-Klasse erstellen, die Datasource von Input erstellen:

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"; 
    } 
} 

Und dann können Sie Datahandler von Input erstellen:

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

Importe:

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

'getInputStream' sollte jedes Mal einen neuen' InputStream' zurückgeben, wenn er – husayt

+0

aufgerufen wird Könnten Sie bitte den Grund dafür erklären? – Gordak

0

(bugs_) Code nicht funktioniert für mich.Ich verwende DataSource zum Erstellen von Anlagen für E-Mails (von Objekten, die inputStream und Name haben) und Inhalt von Anlagen verloren. Sieht so aus, als ob Stefan Recht hat und jeder neue inputStream zurückgegeben werden muss. Zumindest in meinem speziellen Fall. Weiter Umsetzung beschäftigt sich mit Problem:

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

Ich habe treffen Situation, wenn InputStream von DataSource angefordert zweimal: mit Logging Handler zusammen mit MTOM Funktion. Mit this proxy stream solution meine Implementierung funktioniert:

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

hier eine Antwort auf die speziell die Arbeit mit dem Federverschluß org.springframework.core.io.Resource Objekt ist, das ist, wie ich denke, viele von uns sind hier zu bekommen. Beachten Sie, dass Sie möglicherweise den Inhaltstyp im folgenden Code ändern müssen, da ich eine PNG-Datei in eine HTML-formatierte E-Mail einfüge.

Hinweis: Wie andere bereits erwähnt haben, genügt es nicht, lediglich einen InputStream anzuhängen, da dieser mehrmals verwendet wird, nur die Zuordnung zu Resource.getInputStream() macht den Trick.

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"; 
    } 
} 

Verwendung der Klasse sieht wie folgt aus:

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)); 
Verwandte Themen