需求:
现有一个excel文档,该文档从第三行开始,每一行代表一个化石及其相关的登记资料,如图1,现在需要将每一行数据的内容,填写到对应的word文档中,并将入库照片编号对应的照片插入对应位置,如图2,最后要求不能生成单个的word文档,因为文档个数多了,不方便整理和打印,需要合并成一个或者多个;
图1:
图2:
实现思路:
Java语言利用POI读取excel文档,利用Freemarker建立word模板(带图片),循环读取excel每一行数据并生成单个word文档,再利用POI合并成一个word文档。
实现过程:
一、准备模板:参考博客园文章Java 用Freemarker完美导出word文档(带图片)
1、word原件用eclipse或者其他编辑器如Firstobject free XML editor打开;
2、 把需要动态修改的内容替换成********(为了后面方便查找和替换),如果有图片,尽量选择较小的图片几十K左右,并调整好位置(导入图片时,不管什么尺寸的图片,都会按照现在固定好的格式大小及位置导入,也就是会出现图片被拉伸变形,如果没有对应的图片,图片位置会显示如下图);
3、另存为,选择保存类型Word 2003 XML 文档(*.xml)【这里说一下为什么用Microsoft Office Word打开且要保存为Word 2003XML,本人亲测,用WPS找不到Word 2003XML选项,如果保存为Word XML,会有兼容问题,避免出现导出的word文档不能用Word 2003打开的问题】,保存的文件名不要是中文;
4、用Firstobject free XML editor打开文件,选择Tools下的Indent【或者按快捷键F8】格式化文件内容。左边是文档结构,右边是文档内容;
5、 将文档内容中需要动态修改内容的地方,换成freemarker的标识。其实就是Map<String, Object>中key,如${landName}; (注意Map中不能没有对应的key,否则会报错,没有内容的话,value放个空字符串就好了)
6、在加入了图片占位的地方,会看到一片base64编码后的代码,把base64替换成${image},也就是Map<String, Object>中key,值必须要处理成base64;
代码如:<w:binData w:name="wordml://自定义.png" xml:space="preserve">${image}</w:binData>
注意:“>${image}<”这尖括号中间不能加任何其他的诸如空格,tab,换行等符号。
如果需要循环,则使用:<#list maps as map></#list> maps是Map<String, Object>中key,值为数组,map为自定义;
7、标识替换完之后,模板就弄完了,另存为.ftl后缀文件即可。注意:一定不要用word打开ftl模板文件,否则xml内容会发生变化,导致前面的工作白做了。
二、读取excel---生成word文档---合并word文档(源码下载)
小提示:
1、word前后格式要一致,这里代码可能不兼容doc,所以word原文档先另存为docx格式的,后续生成、合并等都要使用docx;
2、excel读取中,每一行的读取时,不要跳过空格单元格,因为向word中写入时,是按照excel中的位置读取的内容;
3、excel读取时,注意框线,有的话,最好是有内容的行列同意都加框线,尤其注意最后一列,缺少框线时读取会有问题;
主程序:
package e2w;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import sun.misc.BASE64Encoder;
/**
* 读取excel文件,把每一行转换为一个word文档数据
*
* @author 15730
*
*/
public class MainOO {
public static int qishibianhao;
public static File file;
public static String pic_url;
public static void main(String[] args) {
Boolean excel_is_exist=false;
Boolean num_is_right=false;
Boolean pic_url_is_right=false;
while(true){
if(excel_is_exist==false){
Scanner s = new Scanner(System.in);
System.out.println("请输入excel文档完整路径,如:C:/Users/15730/Desktop/excel2word/新建 XLS 工作表 - 副本.xls,并按回车键");
String url = s.nextLine();
file = new File(url);
if(!file.exists()){
System.out.println("您输入的文档不存在!");
continue;
}else{
excel_is_exist=true;
}
}
if(num_is_right==false){
Scanner s = new Scanner(System.in);
System.out.println("请输入文档起始编号,1-9999之间的整数,如果输入1,则编号从M1-0001开始,如果输入123,则编号从M1-0123开始,输入完成后请按回车键");
int num = s.nextInt();
if(num>0&&num<10000){
qishibianhao=num;//记录初始编号
num_is_right=true;
}else{
System.out.println("您输入的内容错误!");
continue;
}
}
//图片所在文件夹
if(pic_url_is_right==false){
Scanner s = new Scanner(System.in);
System.out.println("请输入图片存储的完整文件夹名称,如:C:/Users/15730/Desktop/excel2word/tupian/,并按回车键");
String url = s.nextLine();
File file = new File(url);
if(!file.exists()){
System.out.println("您输入的文件夹错误!");
continue;
}else{
if(file.isDirectory()){
pic_url=url;
pic_url_is_right=true;
}else{
System.out.println("您输入的文件夹错误!");
continue;
}
}
}
//全部准备好后,开始执行
if(excel_is_exist==true&&num_is_right==true&&pic_url_is_right==true){
break;
}
}
try {
if(file==null){
System.out.println("文档不存在");
return;
}
List<List<Object>> list_hang = CreatAndReadExcel.readExcel(file);
System.out.println("---------------------------------------------");
System.out.println("本文档行数:"+list_hang.size());
for (int i = 0; i < list_hang.size(); i++) {
if (i < 2) {
continue;
}
String biaoTouBianHao = getBiaoTouBianHao(qishibianhao++);// 表头编号
List<Object> list_lie = list_hang.get(i);
// System.out.println(list_lie.size());
// System.out.println("---------------------------------------------");
// for(int j=0;j<list_lie.size();j++){
// System.out.println(list_lie.get(j));
// }
{
Map<String, Object> map = new HashMap<String, Object>();
map.put("bianhao", biaoTouBianHao);
// String xuHao = list_lie.get(0).toString();//需要&#