Как преобразовать InputStream в DataHandler? - PullRequest
23 голосов
/ 14 мая 2010

Я работаю над Java-приложением, в котором файлы будут храниться в базе данных. Первоначально мы извлекали файлы, уже находящиеся в БД, просто вызывая getBytes в нашем наборе результатов:

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

Этот байтовый массив был затем преобразован в DataHandler с использованием очевидного конструктора:

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

Это прекрасно работало, пока мы не начали пытаться хранить и извлекать файлы большего размера. Выгрузка всего содержимого файла в байтовый массив с последующим построением DataHandler из этого просто требует слишком много памяти.

Моя непосредственная идея - получить поток данных в базе данных с помощью getBinaryStream и каким-то образом преобразовать этот InputStream в DataHandler эффективным способом памяти. К сожалению, не похоже, что есть прямой способ конвертировать InputStream в DataHandler. Другая идея, с которой я играл, - это чтение фрагментов данных из InputStream и запись их в OutputStream из DataHandler. Но ... я не могу найти способ создать "пустой" DataHandler, который возвращает ненулевое OutputStream, когда я звоню getOutputStream ...

Кто-нибудь делал это? Буду признателен за любую помощь, которую вы можете оказать мне, или поведет в правильном направлении.

Ответы [ 7 ]

17 голосов
/ 28 мая 2012

Реализация ответа от "Кэти Ван Стоун":

Сначала создайте вспомогательный класс, который создает DataSource из 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";
    }
}

И затем вы можете создать DataHandler из InputStream:

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

импорт

import javax.activation.DataSource;
import java.io.OutputStream;
import java.io.InputStream;
16 голосов
/ 29 ноября 2010

Я тоже столкнулся с этой проблемой. Если ваши исходные данные byte[], у Axis уже есть класс, который упаковывает InputStream и создает объект DataHandler. Вот код

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

Сопутствующий импорт

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

Надеюсь, это поможет!

16 голосов
/ 14 мая 2010

Мой подход - написать собственный класс, реализующий DataSource, который обернет ваш InputStream. Затем создайте DataHandler, передав ему созданное DataSource.

3 голосов
/ 31 января 2011

Обратите внимание, что getInputStream объекта DataSource должен возвращать новый InputStream при каждом вызове. Это значит, вам нужно скопировать куда-нибудь 1-е. Для получения дополнительной информации см. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4267294

1 голос
/ 11 февраля 2016

(bugs_) код не работает для меня. Я использую DataSource для создания вложений к электронной почте (из объектов, которые имеют inputStream и name ) и содержимого потерянных вложений. Похоже, Стефан прав, и каждый раз должен возвращаться новый inputStream. По крайней мере, в моем конкретном случае. Следующая реализация имеет дело с проблемой:

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 голосов
/ 06 февраля 2018

Вот ответ для конкретной работы с объектом Spring Boot org.springframework.core.io.Resource, который, я думаю, многие из нас получают здесь. Обратите внимание, что вам может потребоваться изменить тип содержимого в приведенном ниже коде, так как я вставляю файл png в электронное письмо в формате html.

Примечание. Как уже упоминалось, простого присоединения InputStream недостаточно, поскольку он используется несколько раз, а просто отображение на 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";
    }
}   

Использование класса выглядит так:

    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 голосов
/ 29 апреля 2017

Я встречался с ситуацией, когда InputStream дважды запрашивал у DataSource: использование обработчика журнала вместе с функцией MTOM. С этим решением для потока прокси моя реализация работает нормально:

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 "";
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...