it-roy-ru.com

Как преобразовать InputStream в DataHandler?

Я работаю над веб-приложением 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...

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

22
pcorey

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

16
Kathy Van Stone

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

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

Я тоже столкнулся с этим вопросом. Если ваши исходные данные - 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
Jorge Pombar

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

3
Stefan

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

}
1
yurin

Вот ответ для конкретной работы с объектом 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
Gandalf

Я сталкивался с ситуацией, когда InputStream дважды запрашивал у DataSource: используя Logging Handler вместе с функцией 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 "";
    }
}
0
GKislin