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) {}}

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 iphone储存将满怎么办 微销通老是请求拒绝怎么办 联璧金融的钱怎么办 店家不给退货怎么办 京东换货不成功怎么办 京东审核关闭怎么办 服装店盘货衣服不对怎么办 盘点衣服少了怎么办 超市盘点少东西怎么办 货到付款买家不收怎么办 快递员忘记收钱怎么办 发货了买家退款怎么办 买家一直不退款怎么办 京东商家退货怎么办 发货后退货邮费怎么办 淘宝货到退款怎么办 淘宝收不到货怎么办 淘宝不处理投诉怎么办 实体店不能退货怎么办 顾客无理由退货怎么办 京东衣服质量问题怎么办 买家退货要运费怎么办 卖家不给退运费怎么办 淘宝申请退款卖家不退怎么办 网购商家报复怎么办 淘宝退货店家不同意怎么办 淘宝退货不同意退款怎么办 运费险没收到怎么办 地税逾期未申报怎么办 转转买家发到付怎么办 捡到苹果手机打不开怎么办 捡到指纹手机打不开怎么办 捡到手机密码打不开怎么办 遇到假苹果售后怎么办 做微商被骗囤货怎么办 代理如果不做了怎么办 微商加盟费不退怎么办 手机背面发霉了怎么办 手机电池被换了怎么办 苹果6pcpu发热怎么办 手机漏电怎么办手发麻