【问题标题】:How to convert an InputStream to a DataHandler?如何将 InputStream 转换为 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...

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

【问题讨论】:

    标签: java inputstream 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;

          【讨论】:

            猜你喜欢
            • 2014-11-10
            • 2011-04-12
            • 2011-12-12
            • 2013-06-11
            • 2016-04-11
            • 2020-02-27
            • 1970-01-01
            • 2010-12-21
            • 1970-01-01
            相关资源
            最近更新 更多