POI, 模板读写和修改, 单元格内换行, 打包成zip
2014-09-29 22:24
549 查看
同上一篇TableTree4J的, 这是我实习一开始就遇到的一个问题.
据说还是已经离职的那位仁兄研究了好久都没有解决的疑难问题呢...
之前没有接触过POI, 研究了一个周末POI的API和源码, 算是曲线解决了= =
因为这个模块在公司的产品中可能以后还会用到, 所以写了一篇说明给公司
本文是在那个基础上修改的
先介绍一下问题的基本情况:
现在有一个模板doc, 里面存放着5个奇怪怪的表格,
我要生成一个新的doc, 一个doc里有好几份这5个奇怪怪的表格, 且表格里每个单元格都填上响应的内容
a)POI核心理念
POI中, 对于每一张表, 表里的每一单元格, 单元格里的每一个段落(包括空段落), 都是一个对象, 其本质是一个xml.
所以可以看到这样的import
b)为什么都写在同一个格子里了?
有一个模板文件后, 要生成多份表格, 很自然的一个想法是在这个文件里复制多份这个表格.
但是这在POI中是行不通的, 你可以复制, 但小到段落大到表格, 复制出来的东西和原来的都储存在同一块堆内存上,
当你试图写入的时候, 表面上是通过栈内存中的不同引用而选中了不同的表格, 但这个引用之后指向的堆内存却是一样的, 其本质上是同一个东西
因此就出现了这样的现象: 复制了很多表, 但后面的表是空的, 所有的东西都在第一张表里.
c)如何解决?(生成临时模板, 保存在服务器上, 再次打开后才写入内容)
解决时需要用到又一个神奇的东西
之前说过, POI的眼里看文件都是对象, 而游标则可以在这些按顺序的对象中穿梭提取
不过首先你得获得一个对象的List:
doc是一个XWPFDocument对象
现在就按需要增加的表格套数来循环增加吧
需要注意的是, j循环是正着循环的, 但实际上是往文档顶部倒序插入表格, 这类似于一个向顶部压栈的过程, 压进去的东西轮流是段落和表格
表格之间若是没有空段落就连在一起了, setTable之后就会变成一张表
d)为什么又放弃了这种写法?
两百多个导出条目的时候就不行了= =, 对象太多了, 堆内存爆掉啦~ 因此采用了打包成zip的方式, 写完一个文件, GC回收一段内存.
e)如何打包成zip?
也是一个文件读写的过程, Java本身也有以下的两个类, 但是中文会乱码, 所以使用了不乱码的:
首先zip中若是有文件名相同的也会有出错, 先利用Set来去重, list储存着文件名:
先新建一个zip文件的普通文件流, 再放入zip文件流:
OutputStream os = new BufferedOutputStream(new FileOutputStream("呵呵呵.zip"));
ZipOutputStream zos = new ZipOutputStream(os);
ZipEntry就是一个要加入的zip的条目, 把他加入到zip文件流中:
还要注意文件流的关闭, 忘记关闭或者位置不对会导致zip的文件无法解压或打开
f)如何在一个单元格内实现换行格式化?
对于数据库中读取的文字, POI写入word时无法识别换行, 因此需要用正则表达式来匹配, 手动实现换行
换行是通过对单元格addParagraph()来实现的
这里需要注意一个特别的函数createRun(), 对一个段落设置文字需要先调用这个哦
g)模板中单元格的行数要变化怎么办?
了解了c)中的实现, 此处也比较简单, 获得一个表格, 增加一行即可
需要理清现在是第几个表格, 第几个行
tbls.get(1).addRow(tbls.get(1).getRow(1));
同上一篇TableTree4J的, 这是我实习一开始就遇到的一个问题.
据说还是已经离职的那位仁兄研究了好久都没有解决的疑难问题呢...
之前没有接触过POI, 研究了一个周末POI的API和源码, 算是曲线解决了= =
因为这个模块在公司的产品中可能以后还会用到, 所以写了一篇说明给公司
本文是在那个基础上修改的
先介绍一下问题的基本情况:
现在有一个模板doc, 里面存放着5个奇怪怪的表格,
我要生成一个新的doc, 一个doc里有好几份这5个奇怪怪的表格, 且表格里每个单元格都填上响应的内容
a)POI核心理念
POI中, 对于每一张表, 表里的每一单元格, 单元格里的每一个段落(包括空段落), 都是一个对象, 其本质是一个xml.
所以可以看到这样的import
import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow;
b)为什么都写在同一个格子里了?
有一个模板文件后, 要生成多份表格, 很自然的一个想法是在这个文件里复制多份这个表格.
但是这在POI中是行不通的, 你可以复制, 但小到段落大到表格, 复制出来的东西和原来的都储存在同一块堆内存上,
当你试图写入的时候, 表面上是通过栈内存中的不同引用而选中了不同的表格, 但这个引用之后指向的堆内存却是一样的, 其本质上是同一个东西
因此就出现了这样的现象: 复制了很多表, 但后面的表是空的, 所有的东西都在第一张表里.
c)如何解决?(生成临时模板, 保存在服务器上, 再次打开后才写入内容)
解决时需要用到又一个神奇的东西
import org.apache.xmlbeans.XmlCursor;
之前说过, POI的眼里看文件都是对象, 而游标则可以在这些按顺序的对象中穿梭提取
不过首先你得获得一个对象的List:
doc是一个XWPFDocument对象
List<XWPFTable> tbls = doc.getTables(); List<XWPFParagraph> paras = doc.getParagraphs();
现在就按需要增加的表格套数来循环增加吧
for (int i = 0; i < pageCount-1; i++) { //i是要增加的模板套数) for (int j = 0, k = 0; j < 5; j++) {//j是一套模板里的表格数, 倒序往word文档顶部插入一套表 XmlCursor paraCursor = doc.getDocument().getBody().getPArray(1).newCursor();//文档顶部有两行回车, 即两个段落, getPArray(1)使得游标获得了第二个段落的位置 doc.insertNewTbl(paraCursor);//在游标位置插入一个表格, 此时原来的第二段就下沉了, 成为此时第一第二张表之间的一个空格, 第二段的位置现在是一个表格对象, 不过现在什么都显示不出来 doc.setTable(0,tbls.get(5+j-k));//将第0个表设置成和第5+j-k一样的表 XmlCursor tblCursor = doc.getDocument().getBody().getTblArray(0).newCursor(); //使得游标获得了第一个表格的位置 doc.insertNewParagraph(tblCursor);//在表格位置插入一个新段落, 文档顶部重新变成了两个空段落 doc.setParagraph(paras.get(0), 0);//将第1个段落设置成和第1个一样的段落 k+=1; } }
需要注意的是, j循环是正着循环的, 但实际上是往文档顶部倒序插入表格, 这类似于一个向顶部压栈的过程, 压进去的东西轮流是段落和表格
表格之间若是没有空段落就连在一起了, setTable之后就会变成一张表
d)为什么又放弃了这种写法?
两百多个导出条目的时候就不行了= =, 对象太多了, 堆内存爆掉啦~ 因此采用了打包成zip的方式, 写完一个文件, GC回收一段内存.
e)如何打包成zip?
也是一个文件读写的过程, Java本身也有以下的两个类, 但是中文会乱码, 所以使用了不乱码的:
import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipOutputStream;
首先zip中若是有文件名相同的也会有出错, 先利用Set来去重, list储存着文件名:
HashSet<String> h = new HashSet<String>(list); list.clear(); list.addAll(h);
先新建一个zip文件的普通文件流, 再放入zip文件流:
OutputStream os = new BufferedOutputStream(new FileOutputStream("呵呵呵.zip"));
ZipOutputStream zos = new ZipOutputStream(os);
ZipEntry就是一个要加入的zip的条目, 把他加入到zip文件流中:
ZipEntry ze = new ZipEntry("完整的文件名.文件名后缀"); zos.putNextEntry(ze);
还要注意文件流的关闭, 忘记关闭或者位置不对会导致zip的文件无法解压或打开
f)如何在一个单元格内实现换行格式化?
对于数据库中读取的文字, POI写入word时无法识别换行, 因此需要用正则表达式来匹配, 手动实现换行
换行是通过对单元格addParagraph()来实现的
这里需要注意一个特别的函数createRun(), 对一个段落设置文字需要先调用这个哦
private void splitAndWrite(String str, XWPFTableCell cell){ if(null == str){ }else{ String []s = str.split("[\n\r]");//按回车符分割字符 if (s.length==1) { cell.setText(str); }else{ cell.setText(s[0]); for (int i = 1; i < s.length; i++) { XWPFParagraph p = cell.addParagraph();//添加新段落 p.createRun().setText(s[i]); } } } }
g)模板中单元格的行数要变化怎么办?
了解了c)中的实现, 此处也比较简单, 获得一个表格, 增加一行即可
需要理清现在是第几个表格, 第几个行
tbls.get(1).addRow(tbls.get(1).getRow(1));
相关文章推荐
- 用POI在word07模板文件中创建表格,修改内容等操作
- 修改servlet和jsp模板.zip
- POI根据EXCEL模板,修改内容导出新EXCEL
- 1 npoi 网上 不用模板 设置密码 workbook.WriteProtectWorkbook("password", "admin"); 、、 2 locked.IsLocked = true; sheet1.ProtectSheet("password");NPOI操作EXCEL--设置密码才可以修改单元格内容 3 模板设置密码 确定原密码 设置新密码
- poi-11单元格中使用换行
- poi-3.8修改源码、编译、打包过程
- Java代码用POI读写Excel并修改excel格式
- poi 单元格内容换行
- 使用POI操作Excel修改模板(批量替换excel中的数据)
- [转]POI读写Excel 修改
- POI:字体、读取和重写、单元格换行、用户自定义格式
- POI中设置Excel单元格格式(背景色,居中,字体,边框,列宽,行高,自动换行等)
- 使用POI操作Excel修改模板(批量替换excel中的数据并判断excel版本)
- POI根据EXCEL模板,修改内容导出新EXCEL (只支持HSSF)
- POI——动态修改Excel模板下拉框
- poi-3.8修改源码、编译、打包过程
- poi导出word 表格 单元格内换行
- poi读取模板并修改模板内容
- poi操作excel导出是否修改模板文件内容的问题
- 修改POI中公式不能显示中文的方法