OutputStream转InputStream的两种方案
来源:互联网 发布:单簧管和长笛 知乎 编辑:程序博客网 时间:2024/06/06 05:28
项目中需要将动态数据填充到模板中后生成的文件与其他用户上传的文件打包供审批人员下载,思路简单。但问题来了,利用freemaker将动态数据填充玩后会将流写入一个给定的OutputStream中,如果将此文件流存入物理硬盘,打包的时候再去下载。对物理硬盘的IO性能差不说,还占资源,果断否决。
方案一: ByteArrayOutputStream
此方案简单粗爆,如果遇到OutputStream写入的数据非常大的时间,就苦逼了,有内存溢出的风险,所以,勿必慎用。
public void testFrByteArray() throws Exception {// 业务对象,此处仅给空对象做示例,Freemarker将此model解析替换模板中的标签,生成业务文件.Object model = new Object();// 新建字节输出流,Freemarker操作此输出流写入生成的业务文件.ByteArrayOutputStream out = new ByteArrayOutputStream();// 自定义工具类,封装Freemarker操作细节FreemarkerUtils.renderTemplate(out, model, "template.xml");// 将outputstream转成inputstreamByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());FileOutputStream fos = new FileOutputStream("D:\\test\result.rar");ZipOutputStream zip = new ZipOutputStream(fos);// 生成压缩文件ZipUtils.add(zip, in, "template-res.doc", true);}
方案二:PipedInputStream与PipedOutputStream
此方案有点高大上,但操作和理解起来有点难度。先看一下官方API的说明:
PipedInputStream,管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从PipedInputStream
对象读取, 并由其他线程将其写入到相应的PipedOutputStream
。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的 范 围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
红色字体部分很重要,因为很不巧笔者在这上面摔跟头了。
PipedOutputStream,可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream
对象,并由其他线程从连接的PipedInputStream
读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态。
通过官方API的两段解释,大概能摸到这种实现方式的设计思路了:PipedOutputStream(生产者)生产数据,PipedInputStream(消费者)读取数据,二者同步进行。但谁也不能先挂掉,一旦挂掉,管道便处于损坏状态。
public void testFrPiped() throws IOException {// 业务对象,此处仅给空对象做示例,Freemarker将此model解析替换模板中的标签,生成业务文件.final Object model = new Object();FileOutputStream fos = new FileOutputStream("D:\\test\result.rar");final ZipOutputStream zip = new ZipOutputStream(fos);final PipedInputStream pis = new PipedInputStream();final PipedOutputStream pos = new PipedOutputStream();//将管道输出流连接到管道输入流来创建通信管道pos.connect(pis);//创建生产者线程来为管道输出流写入数据.new Thread(new Runnable() {public void run() {try {FreemarkerUtils.renderTemplate(pos, model,"testtemplate.xml");} catch (IOException e) {} catch (TemplateException e) {}}}).start();//创建消费者线程来从 PipedInputStream对象读取数据Thread customer = new Thread(new Runnable() {public void run() {try {ZipUtils.add(zip, pis, "testtemplate.doc", true);} catch (IOException e) {}}});customer.start();try {//勿必等所有数据读完才继续后面的操作,否则可能造成数据仍未读完便GameOver了.customer.join();} catch (InterruptedException e) {}}
- OutputStream转InputStream的两种方案
- 通俗易懂的InputStream、OutputStream
- OutputStream 转 InputStream
- inputStream转outputStream
- InputStream与OutputStream的比较
- InputStream与OutputStream的比较
- InputStream与OutputStream的比较
- InputStream与OutputStream的比较
- InputStream与OutputStream的区分
- InputStream和OutputStream的比较
- InputStream和OutputStream的理解
- InputStream与OutputStream的比较
- InputStream与OutputStream的比较
- InputStream与OutputStream的比较
- InputStream OutputStream 源的理解
- InputStream与OutputStream的比较
- InputStream与OutputStream的转化
- inputstream 转 string 转 outputstream
- 圆角边的实现
- 滑动窗口与拥塞窗口
- android4.4对于下载里面有些apk文件无法打开的修改
- Spring框架4的改进
- Unity3D与Android相互传递消息
- OutputStream转InputStream的两种方案
- java枚举类型详解
- SSH三大框架的作用。
- ffmpeg 内存转码
- 知识共享平台开发-BUG[2014-11-27]
- 上班无聊,可以自己学习下别的知识或看看自己以前写的代码有没有好的改进的地方
- 真机调试报错gen_entitlements.py: No such file or directory
- Java---快排
- windows 下 搭建redis数据库 和 php安装redis 扩展