2010-05-13 12 views
19

Je travaille sur une application web Java dans laquelle les fichiers seront stockés dans une base de données. A l'origine nous avons récupéré les fichiers déjà dans la base de données en appelant simplement getBytes sur notre jeu de résultats:Comment convertir un InputStream en DataHandler?

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

Ce tableau d'octets a ensuite été converti en un DataHandler utilisant le constructeur évident:

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

Cela a très bien fonctionné jusqu'à ce que nous commencé à essayer de stocker et de récupérer des fichiers plus volumineux. Déposer tout le contenu du fichier dans un tableau d'octets, puis en construire un DataHandler nécessite simplement trop de mémoire.

Mon idée immédiate est de récupérer un flux de données dans la base de données avec getBinaryStream et de convertir en quelque sorte ce InputStream en un DataHandler d'une manière efficace de la mémoire. Malheureusement, il ne semble pas y avoir de moyen direct de convertir un InputStream en un DataHandler. Une autre idée avec laquelle j'ai joué est de lire des morceaux de données du InputStream et de les écrire sur le OutputStream du DataHandler. Mais ... je ne peux pas trouver un moyen de créer un "vide" DataHandler qui renvoie un non-nul OutputStream quand j'appelle getOutputStream ...

Quelqu'un at-il fait cela? J'apprécierais toute aide que vous pourriez me donner ou diriger dans la bonne direction.

Répondre

14

Mon approche serait d'écrire une classe personnalisée implémentant DataSource qui enveloppe votre InputStream. Puis créez le DataHandler en lui donnant le DataSource créé.

+0

Ah, c'est une excellente idée. Je vais essayer quand j'ai une chance. – pcorey

+0

Je pensais la même chose. Mais attention, alors le DataHandler doit être utilisé (consomme son entrée), "inside you loop", tandis que le ResultSet est ouvert. Par exemple, vous ne pouvez probablement pas passer l'objet DataHandler à une couche supérieure. – leonbloy

+0

@leonbloy L'objectif déclaré était de traiter les données sans les copier à partir du jeu de résultats. Cela implique que le jeu de résultats doit être ouvert tout le temps, peu importe comment vous le faites. –

16

J'ai également rencontré ce problème. Si vos données source sont byte[] Axis possède déjà une classe qui enveloppe l'InputStream et crée un objet DataHandler. Voici le code

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

importations liées

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

Hope it helps!

+3

Puisqu'il charge toutes les données en mémoire, cela causerait des problèmes lors de la gestion de grandes données. –

3

Notez que le getInputStream du DataSource doit renvoyer un nouvel InputStream à chaque appel. Cela signifie que vous devez copier quelque part le 1er. Pour plus d'informations, consultez http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4267294

+0

Je sais que c'est vieux ... est ce bug réel? – Cris

+2

L'API dit que. Cependant, il dit retourner un nouveau flux ou lancer une exception. Techniquement, cela signifie retourner un flux la première fois et ensuite lancer des exceptions. Je suppose que la plupart des frameworks ne récupèrent le flux qu'une seule fois. – Steve11235

14

Une mise en œuvre de la réponse de "Kathy Van Stone":

Tout d'abord créer la classe d'aide, ce qui crée DataSource 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"; 
    } 
} 

Et puis vous pouvez créer DataHandler de InputStream:

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

importations:

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

'getInputStream' devrait retourner un nouveau' InputStream' chaque fois qu'il est appelé – husayt

+0

Pourriez-vous expliquer la raison de ceci s'il vous plaît? – Gordak

0

(bugs_) code ne fonctionne pour moi.J'utilise DataSource pour créer des pièces jointes à envoyer par courrier électronique (à partir d'objets qui ont inputStream et nom) et le contenu des pièces jointes perdues. On dirait que Stefan a raison et que inputStream doit être retourné à chaque fois. Au moins dans mon cas particulier. Suivant offres de mise en œuvre avec le problème:

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

J'ai situation se rencontrent, quand InputStream a demandé de DataSource deux fois: en utilisant Gestionnaire d'enregistrement avec fonction de MMD. Avec this proxy stream solution ma mise en œuvre fonctionne très 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

Voici une réponse pour travailler spécifiquement avec l'objet Spring Boot org.springframework.core.io.Resource qui est la façon dont je pense que beaucoup d'entre nous obtiennent ici. Notez que vous devrez peut-être modifier le type de contenu dans le code ci-dessous car j'insère un fichier png dans un e-mail au format HTML.

Note: Comme d'autres l'ont mentionné, il suffit d'attacher un InputStream car il est utilisé plusieurs fois, il suffit de faire un mapping vers Resource.getInputStream().

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

Utilisation de la classe ressemble à ceci:

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