现有一个excel文档,该文档从第三行开始,每一行代表一个化石及其相关的登记资料,如图1,现在需要将每一行数据的内容,填写到对应的word文档中,并将入库照片编号对应的照片插入对应位置,如图2,最后要求不能生成单个的word文档,因为文档个数多了,不方便整理和打印,需要合并成一个或者多个;
图1:
图2:
Java语言利用POI读取excel文档,利用Freemarker建立word模板(带图片),循环读取excel每一行数据并生成单个word文档,再利用POI合并成一个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
6、在加入了图片占位的地方,会看到一片base64编码后的代码,把base64替换成${image},也就是Map
代码如:
注意:“>${image}<”这尖括号中间不能加任何其他的诸如空格,tab,换行等符号。
如果需要循环,则使用:<#list maps as map>#list> maps是Map
7、标识替换完之后,模板就弄完了,另存为.ftl后缀文件即可。注意:一定不要用word打开ftl模板文件,否则xml内容会发生变化,导致前面的工作白做了。
小提示:
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_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
excel操作:
package e2w;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
/**
* 可以从http://poi.apache.org/ 这里下载到POI的jar�?POI 创建和读�?003-2007版本Excel文件
*
*/
public class CreatAndReadExcel {
public static void main(String[] args) throws Exception {
// creat2003Excel();// 创建2007版Excel文件
// creat2007Excel();// 创建2003版Excel文件
// 读取2003Excel文件
String path2003 = System.getProperty("user.dir")
+ System.getProperty("file.separator") + "style_2003.xls";// 获取项目文件路径
// +2003版文件名
System.out.println("路径:" + path2003);
File f2003 = new File(path2003);
try {
readExcel(f2003);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// //读取2007Excel文件
// String path2007 = System.getProperty("user.dir")
// + System.getProperty("file.separator") + "style_2007.xlsx";//
// 获取项目文件路径+2007版文件名
// // System.out.println("路径�? + path2007);
// File f2007 = new File(path2007);
// try {
// readExcel(f2007);
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
/**
* 创建2007版Excel文件
*
* @throws FileNotFoundException
* @throws IOException
*/
private static void creat2007Excel() throws FileNotFoundException,
IOException {
// HSSFWorkbook workBook = new HSSFWorkbook();// 创建 �?��excel文档对象
XSSFWorkbook workBook = new XSSFWorkbook();
XSSFSheet sheet = workBook.createSheet();// 创建�?��工作薄对�?
sheet.setColumnWidth(0, 10000);// 设置第二列的宽度�?
XSSFRow row = sheet.createRow(1);// 创建�?��行对�?
row.setHeightInPoints(23);// 设置行高23像素
XSSFCellStyle style = workBook.createCellStyle();// 创建样式对象
// 设置字体
XSSFFont font = workBook.createFont();// 创建字体对象
font.setFontHeightInPoints((short) 15);// 设置字体大小
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);// 设置粗体
font.setFontName("黑体");// 设置为黑体字
style.setFont(font);// 将字体加入到样式对象
// 设置对齐方式
style.setAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION);// 水平居中
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);// 垂直居中
// 设置边框
style.setBorderTop(HSSFCellStyle.BORDER_THICK);// 顶部边框粗线
style.setTopBorderColor(HSSFColor.RED.index);// 设置为红�?
style.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);// 底部边框双线
style.setBorderLeft(HSSFCellStyle.BORDER_MEDIUM);// 左边边框
style.setBorderRight(HSSFCellStyle.BORDER_MEDIUM);// 右边边框
// 格式化日�?
style.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
XSSFCell cell = row.createCell(1);// 创建单元�?
cell.setCellValue(new Date());// 写入当前日期
cell.setCellStyle(style);// 应用样式对象
// 文件输出�?
FileOutputStream os = new FileOutputStream("style_2007.xlsx");
workBook.write(os);// 将文档对象写入文件输出流
os.close();// 关闭文件输出�?
System.out.println("创建成功 office 2007 excel");
}
/**
* 创建2003版本的Excel文件
*/
public static void creat2003Excel(String path)
throws FileNotFoundException, IOException {
HSSFWorkbook workBook = new HSSFWorkbook();// 创建 一个excel文档对象
HSSFSheet sheet = workBook.createSheet();// 创建 一个工作薄对象?
sheet.setColumnWidth(0, 3000);// 设置第1列的宽度
sheet.setColumnWidth(1, 5000);// 设置第2列的宽度
sheet.setColumnWidth(2, 3000);// 设置第3列的宽度
// HSSFRow row = sheet.createRow(1);// 创建一个行对象
// row.setHeightInPoints(23);// 设置行高23像素
// HSSFCellStyle style = workBook.createCellStyle();// 创建样式对象
// 设置字体
// HSSFFont font = workBook.createFont();// 创建字体对象
// font.setFontHeightInPoints((short) 15);// 设置字体大小
// font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);// 设置粗体
// font.setFontName("黑体");// 设置为黑体字
// style.setFont(font);// 将字体加入到样式对象
// 设置对齐方式
// style.setAlignment(HSSFCellStyle.ALIGN_CENTER_SELECTION);// 水平居中
//
// style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);// 垂直居中
// 设置边框
// style.setBorderTop(HSSFCellStyle.BORDER_THICK);// 顶部边框粗线
//
// style.setTopBorderColor(HSSFColor.RED.index);// 设置为红�?
//
// style.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);// 底部边框双线
//
// style.setBorderLeft(HSSFCellStyle.BORDER_MEDIUM);// 左边边框
//
// style.setBorderRight(HSSFCellStyle.BORDER_MEDIUM);// 右边边框
// 格式化日�?
// style.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
//
// HSSFCell cell = row.createCell(1);// 创建单元�?
// cell.setCellValue(new Date());// 写入当前日期
// cell.setCellStyle(style);// 应用样式对象
// 文件输出�?
FileOutputStream os = new FileOutputStream(path);
workBook.write(os);// 将文档对象写入文件输出流
os.close();// 关闭文件输出�?
// System.out.println("创建成功 office 2003 excel");
}
/**
* 对外提供读取excel 的方�?
*/
public static List> readExcel(File file) throws IOException {
String fileName = file.getName();
String extension = fileName.lastIndexOf(".") == -1 ? "" : fileName
.substring(fileName.lastIndexOf(".") + 1);
if ("xls".equals(extension)) {
return read2003Excel(file);
} /*
* else if ("xlsx".equals(extension)) { return read2007Excel(file); }
*/else {
throw new IOException("不支持的文件类型");
}
}
/**
* 读取 office 2003 excel
*
* @throws IOException
* @throws FileNotFoundException
*/
private static List> read2003Excel(File file)
throws IOException {
List> list = new LinkedList>();
HSSFWorkbook hwb = new HSSFWorkbook(new FileInputStream(file));
HSSFSheet sheet = hwb.getSheetAt(0);
Object value = null;
HSSFRow row = null;
HSSFCell cell = null;
System.out.println("读取office 2003 excel内容如下: ");
for (int i = sheet.getFirstRowNum(); i <= sheet
.getPhysicalNumberOfRows(); i++) {
row = sheet.getRow(i);
if (row == null) {
continue;
}
List
word操作:
package e2w;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import freemarker.template.Configuration;
import freemarker.template.Template;
public class WordUtils {
// 配置信息,代码本身写的还是很可读的,就不过多注解了
private static Configuration configuration = null;
// 这里注意的是利用WordUtils的类加载器动态获得模板文件的位置
private static final String templateFolder = WordUtils.class
.getClassLoader().getResource("").getPath()
+ "templates/";
// private static final String templateFolder =
// "D:\\java_old\\workspace\\excel2Word\\bin\\templates";
static {
configuration = new Configuration();
configuration.setDefaultEncoding("utf-8");
try {
configuration.setDirectoryForTemplateLoading(new File(
templateFolder));
} catch (IOException e) {
e.printStackTrace();
}
}
private WordUtils() {
throw new AssertionError();
}
// public static void exportMillCertificateWord(HttpServletRequest request,
// HttpServletResponse response, Map map,String title,String ftlFile) throws
// IOException {
// Template freemarkerTemplate = configuration.getTemplate(ftlFile);
// File file = null;
// InputStream fin = null;
// ServletOutputStream out = null;
// try {
// // 调用工具类的createDoc方法生成Word文档
// file = createDoc(map,freemarkerTemplate);
// fin = new FileInputStream(file);
//
// response.setCharacterEncoding("utf-8");
// response.setContentType("application/msword");
// // 设置浏览器以下载的方式处理该文件名
// String fileName = title+DateUtil.formatDateDetailTime(new Date()) +
// ".doc";
// response.setHeader("Content-Disposition", "attachment;filename="
// .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
//
// out = response.getOutputStream();
// byte[] buffer = new byte[512]; // 缓冲区
// int bytesToRead = -1;
// // 通过循环将读入的Word文件的内容输出到浏览器中
// while((bytesToRead = fin.read(buffer)) != -1) {
// out.write(buffer, 0, bytesToRead);
// }
// } finally {
// if(fin != null) fin.close();
// if(out != null) out.close();
// if(file != null) file.delete(); // 删除临时文件
// }
// }
private static File createDoc(Map, ?> dataMap, String title,
Template template) {
String fileName = title + ".docx";
File f = new File(fileName);
Template t = template;
try {
// 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
t.process(dataMap, w);
w.close();
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
return f;
}
public static void exportWord(Map, ?> map, String title, String ftlFile)
throws IOException {
System.out.println("-------------------- " + templateFolder);
Template freemarkerTemplate = configuration.getTemplate(ftlFile);
// 调用工具类的createDoc方法生成Word文档
File file = createDoc(map, title, freemarkerTemplate);
}
}
package e2w;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.Document;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody;
/**
* @author: Max
*
* @Date: 2018/6/8
*
* @name: 多个word文件合并,采用poi实现,兼容图片的迁移
*
* @Description:
*/
public class MergeDoc {
public static void main (String[] args) throws Exception {
InputStream in1 = null;
InputStream in2 = null;
InputStream in3 = null;
OPCPackage src1Package = null;
OPCPackage src2Package = null;
OPCPackage src3Package = null;
OutputStream dest = new FileOutputStream("C:/Users/15730/Desktop/excel2word/merge123.docx");
try {
in1 = new FileInputStream("C:/Users/15730/Desktop/excel2word/M1-0001.docx");
in2 = new FileInputStream("C:/Users/15730/Desktop/excel2word/M1-0002.docx");
in3 = new FileInputStream("C:/Users/15730/Desktop/excel2word/M1-0003.docx");
src1Package = OPCPackage.open(in1);
src2Package = OPCPackage.open(in2);
src3Package = OPCPackage.open(in3);
} catch (Exception e) {
e.printStackTrace();
}
XWPFDocument src1Document = new XWPFDocument(src1Package);
XWPFDocument src2Document = new XWPFDocument(src2Package);
XWPFDocument src3Document = new XWPFDocument(src3Package);
appendBody(src1Document, src2Document);
appendBody(src1Document, src3Document);
src1Document.write(dest);
}
public static void appendBody(XWPFDocument src, XWPFDocument append) throws Exception {
CTBody src1Body = src.getDocument().getBody();
CTBody src2Body = append.getDocument().getBody();
List allPictures = append.getAllPictures();
// 记录图片合并前及合并后的ID
Map map = new HashMap();
for (XWPFPictureData picture : allPictures) {
String before = append.getRelationId(picture);
//将原文档中的图片加入到目标文档中
String after = src.addPictureData(picture.getData(), Document.PICTURE_TYPE_PNG);
map.put(before, after);
}
appendBody(src1Body, src2Body,map);
}
private static void appendBody(CTBody src, CTBody append,Map map) throws Exception {
XmlOptions optionsOuter = new XmlOptions();
optionsOuter.setSaveOuter();
String appendString = append.xmlText(optionsOuter);
String srcString = src.xmlText();
String prefix = srcString.substring(0,srcString.indexOf(">")+1);
String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
String sufix = srcString.substring( srcString.lastIndexOf("<") );
String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
if (map != null && !map.isEmpty()) {
//对xml字符串中图片ID进行替换
for (Map.Entry set : map.entrySet()) {
addPart = addPart.replace(set.getKey(), set.getValue());
}
}
//将两个文档的xml内容进行拼接
CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
src.set(makeBody);
}
}
在打包程序时,遇到了一些问题,见另外两篇博客:
1、eclipse导出可运行jar包时,双击没反应,命令行中jar命令运行报错“**.jar中没有主清单属性”或者报错“Error: Invalid or corrup jarfile **.jar。
2、MANIFEST.MF文件详解