web-dev-qa-db-fra.com

Comment convertir un InputStream en un DataHandler?

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é des fichiers déjà dans la base de données en appelant simplement getBytes dans notre jeu de résultats:

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

Ce tableau d'octets a ensuite été converti en DataHandler à l'aide du constructeur évident:

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

Cela a très bien fonctionné jusqu'à ce que nous essayions de stocker et de récupérer des fichiers plus volumineux. Déposer tout le contenu du fichier dans un tableau d'octets, puis créer un DataHandler à partir de cela 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 de manière à économiser la mémoire. Malheureusement, il ne semble pas qu'il existe un moyen direct de convertir un InputStream en un DataHandler. Une autre idée à laquelle je joue est de lire des fragments de données de InputStream et de les écrire dans le OutputStream du DataHandler. Mais ... je ne trouve pas le moyen de créer un DataHandler "vide" qui renvoie un OutputStream non nul lorsque j'appelle getOutputStream...

Quelqu'un l'a-t-il fait? J'apprécierais toute aide que vous pouvez me donner ou qui mène dans la bonne direction.

22
pcorey

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

16
Kathy Van Stone

Une implémentation de la réponse de "Kathy Van Stone":

Au début, créez la classe d'assistance, qui crée DataSource à partir 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 vous pouvez ensuite créer DataHandler à partir de InputStream:

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

importations :

import javax.activation.DataSource;
import Java.io.OutputStream;
import Java.io.InputStream;
17
bugs_

J'ai aussi rencontré ce problème. Si vos données source sont un byte[], Axis a déjà une classe qui englobe 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 connexes

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

J'espère que ça aide!

16
Jorge Pombar

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

3
Stefan

(bugs_) le code ne fonctionne pas pour moi. J'utilise DataSource pour créer des pièces jointes à un courrier électronique (à partir d'objets qui ont inputStream etnom ) et le contenu des pièces jointes perdues . On dirait que Stefan a raison et qu'un nouveau inputStream doit être renvoyé à chaque fois. Au moins dans mon cas particulier. La prochaine implémentation traite du 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");
    }

}
1
yurin

Voici une réponse pour travailler spécifiquement avec l’objet Spring Boot org.springframework.core.io.Resource, ce qui est, selon moi, à quel point nous sommes nombreux à en arriver là. 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 courrier électronique au format HTML. 

Remarque: comme d'autres l'ont mentionné, le simple fait de connecter un InputStream n'est pas suffisant, car il est utilisé plusieurs fois. Un mappage vers Resource.getInputStream () suffit.

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

L'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));
0
Gandalf

J'ai rencontré une situation où InputStream a demandé deux fois à DataSource: utiliser Logging Handler avec la fonction MTOM . Avec this solution de flux proxy , ma mise en œuvre fonctionne 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
GKislin