如何将 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
读取数据块并将它们写入DataHandler
的OutputStream
。但是...我找不到创建“空”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 创建电子邮件附件(来自具有 inputStream 和 name 的对象)和附件内容丢失。 看起来 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】:当InputStream
向DataSource
请求两次时,我遇到了这种情况:使用 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();
可能的错误:元标记必须关闭。 <meta></meta>
【讨论】:
以上是关于如何将 InputStream 转换为 DataHandler?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 InputStream 转换为 InputSource?
如何将 Reader 转换为 InputStream 并将 Writer 转换为 OutputStream?
如何将 Source[ByteString, Any] 转换为 InputStream
将 inputStream 转换为 FileInputStream?