如何将 InputStream 转换为 DataHandler?

Posted

技术标签:

【中文标题】如何将 InputStream 转换为 DataHandler?【英文标题】:How to convert an InputStream to a DataHandler? 【发布时间】:2011-02-19 07:24:29 【问题描述】:

我正在开发一个将文件存储在数据库中的 Java Web 应用程序。最初,我们通过简单地在结果集上调用getBytes 来检索数据库中已经存在的文件:

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

然后使用明显的构造函数将这个字节数组转换为DataHandler

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

在我们开始尝试存储和检索更大的文件之前,这非常有效。将整个文件内容转储到一个字节数组中,然后从中构建一个DataHandler 只需要太多内存。

我的直接想法是使用getBinaryStream 检索数据库中的数据流,并以某种内存有效的方式将InputStream 转换为DataHandler。不幸的是,似乎没有将InputStream 转换为DataHandler 的直接方法。我一直在玩的另一个想法是从InputStream 读取数据块并将它们写入DataHandlerOutputStream。但是...我找不到创建“空”DataHandler 的方法,当我调用getOutputStream 时返回非空OutputStream...

有人做过吗?如果您能给我提供任何帮助或指引正确的方向,我将不胜感激。

【问题讨论】:

【参考方案1】:

我的方法是编写一个实现DataSource 的自定义类,该类包装您的InputStream。然后创建DataHandler 给它创建的DataSource

【讨论】:

啊,这是个好主意。有机会我会试试的。 我也是这么想的。但请注意,当 ResultSet 处于打开状态时,必须在“循环内部”使用 DataHandler(使用其输入)。例如,您可能无法将 DataHandler 对象传递给上层。 @leonbloy 既定目标是在不从结果集中复制数据的情况下处理数据。这意味着无论您如何操作,结果集都必须始终打开。【参考方案2】:

我也遇到了这个问题。如果您的源数据是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;

希望对你有帮助!

【讨论】:

由于它会将所有数据加载到内存中,因此在管理大数据时会出现问题。 DataSource 接口还有其他实现:我使用了 import javax.mail.util.ByteArrayDataSource;【参考方案3】:

注意DataSource的getInputStream每次调用都必须返回一个新的InputStream。这意味着,您需要在第一个地方复制。 有关详细信息,请参阅 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4267294

【讨论】:

我知道它是旧的......那个错误是真的吗? API 这么说。但是,它说返回一个新流或抛出异常。从技术上讲,这意味着第一次返回一个流,然后抛出异常。我假设大多数框架只检索一次流。【参考方案4】:

“Kathy Van Stone”回答的实现:

首先创建辅助类,从 InputStream 创建 DataSource:

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

然后你可以从 InputStream 中创建 DataHandler:

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

进口

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

【讨论】:

getInputStream 应该在每次调用时返回一个新的InputStream 您能解释一下原因吗? 因为重用 InputStream 可能会出现“Stream Closed”的 IOException 通用 InputStream 只能读取一次,没有理由将其包装到新的流中。 可以使用 org.apache.cxf.attachment.AttachmentDataSource 而不是这个答案中的类 (ct = ContentType)【参考方案5】:

(bugs_) 代码对我不起作用。我使用 DataSource 创建电子邮件附件(来自具有 inputStreamname 的对象)和附件内容丢失。 看起来 Stefan 是对的,每次都必须返回新的 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");
    


【讨论】:

【参考方案6】:

InputStreamDataSource 请求两次时,我遇到了这种情况:使用 Logging Handler 和 MTOM 功能。 使用this proxy stream solution 我的实现工作正常:

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

【讨论】:

【参考方案7】:

这是一个专门使用 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));

【讨论】:

【参考方案8】:
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

.

 DataSource ds = new ByteArrayDataSource(convertHtmlToPdf("<span>html here</span>"), "application/pdf");

 DataHandler dataHandler = new DataHandler(ds);

.

public static byte[] convertHtmlToPdf(String htmlString) throws IOException, DocumentException 
    Document document = new Document();

    ByteArrayOutputStream out = new ByteArrayOutputStream();

    PdfWriter writer = PdfWriter.getInstance(document, out);
    document.open();

    InputStream in = IOUtils.toInputStream(htmlString);
    XMLWorkerHelper.getInstance().parseXHtml(writer, document, in);
    document.close();

    return out.toByteArray();

可能的错误:元标记必须关闭。 &lt;meta&gt;&lt;/meta&gt;

【讨论】:

以上是关于如何将 InputStream 转换为 DataHandler?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 InputStream 转换为 InputSource?

如何将 Reader 转换为 InputStream 并将 Writer 转换为 OutputStream?

如何将 Source[ByteString, Any] 转换为 InputStream

将 inputStream 转换为 FileInputStream?

在 Java 中,如何将 InputStream 转换为字节数组 (byte[])? [复制]

如何将 inputsource 转换为 inputstream 并将其作为参数提供给 stringreader?