从零开始—我的开源项目5:代码完善(中)

既然使用的是csdn的博客,自然要给其来篇软文,多个广告啥的。

本项目已托管于https://code.csdn.net/代码托管平台,附git地址git://code.csdn.net/wangyuheng77/excel2word.git

本文除了功能完善与代码优化外,还会介绍如何将程序打包成jar包以及生成.exe文件。


模板的问题有两点:

第一点就是神奇的word格式。在另存为的过程中,一不小心(如:删除又增加一个字符),就会多余的格式标签。

另一个问题就是,模板的制作过程。居然需要写完这么复杂的标签后,还需要完成另存为.xml这么高难度的事情,简直是泯灭人性。发火

针对这两点,我一开始的思路是:

读取word文件,替换标签,io生成.xml文件,freemarker替换生成word文件。

这个尝试过程略过,在经历了痛苦的尝试之后,我终于醒悟(请原谅我的愚笨):为毛一定要用template.xml和freemarker啊?!

也许是网上大神们给的参考文章,让人变得懒得去思考。但现在静下心来一想,完全可以在读取word文件之后,替换固定标签,再通过io流生成新的word文件。

难点在于替换固定标签,不然这就是一个简单的io复制操作。


思路确定后,还是一步一步来,先从excel文件的数据读取开始,也就是传说中的XlsUtil.java。

之前是按照列读取,并且把第一列默认为文件名,实在是不够灵活。

这次给出的解决思路是:第一行作为标签的key,如


这里有两点需要注意的地方:

1、空格列,空格列,需要略过这列的内容。

2、非String格式,如1会被转换为1.0.解决方法:row.getCell(i).setCellType(Cell.CELL_TYPE_STRING);

读取到的map,key已经加上了我们指定的标签格式,如{“#{name}”:“zhangsan”,#{tel}:110}

关于生成的word文件命名的问题,我给出的方案是:如果用户未在excel文件中指定name标签(上图A1格),则默认第一列为name。

XlsUtil代码如下:

package excel2word.utils;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

public class XlsUtil {

	public static Map<String, Object> extractRow(Row row, List<String> keys){
		Map<String, Object> params = new HashMap<String, Object>();
		for (int i=0; i<=row.getLastCellNum(); i++) {
			if (StringUtils.isBlank(keys.get(i))) {
				continue;
			}
			params.put("#{"+keys.get(i)+"}", row.getCell(i));
		}
		return params; 
	}
	
	/**
	 * 第一行作为key,即mark标签名
	 */
	private static List<String> getKeyList(Row row) {
		List<String> keys = new ArrayList<String>();
		for (int i=0; i<row.getLastCellNum();i++) {
			if (row.getCell(i) != null) {
				row.getCell(i).setCellType(Cell.CELL_TYPE_STRING);
			} 
			keys.add(row.getCell(i)==null?"":row.getCell(i).toString().toLowerCase());
		}
		//如果未指定name标签,则默认第一列为生成的word文件名
		if (!keys.contains("name")) {
			keys.set(0, "name");
		}
		return keys;
	}
	
	public static List<Map<String, Object>> extractXls(FileInputStream fis) throws IOException{
		List<Map<String, Object>> results = new ArrayList<Map<String,Object>>();
		try {
			Workbook book = new HSSFWorkbook(new BufferedInputStream(fis));
			Sheet sheet = book.getSheetAt(0);
			if (sheet.getLastRowNum() == 0) {
				return null;
			}
			List<String> keys = getKeyList(sheet.getRow(0));
			for (int i=sheet.getFirstRowNum()+1; i <= sheet.getLastRowNum(); i++) {
				Map<String, Object> param = extractRow(sheet.getRow(i), keys);
				results.add(param);
			}
		} catch (IOException e) {
			throw e;
		} 
		return results;
	}
	
}

然后是替换标签,生成word文件。

采用POI已经封装好的方法,HWPFDocument类,获取Range,然后采用range.replaceText()替换标签,最后再由HWPFDocument做write操作。

流程很简单,代码也简单,一看就明白了

DocUtil代码如下:

package excel2word.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;

import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;

import excel2word.config.constants.WorkConstant;
import excel2word.config.enumerations.FileType;

public class DocUtil {

	private static File getRenameFile(String path, FileType type, String docName) {
		//解决重名问题
		int i = 1;
		File doc = new File(path + docName+type.getLabel());
		while (doc.exists()) {
			doc = new File(path + docName+"("+i+")"+type.getLabel());
			i++;
		}
		return doc;
	} 
	
	public static void export(Map<String, Object> params, File template, FileType exportType, String exportName) {
		FileOutputStream fos = null;
		try {
			HWPFDocument doc = new HWPFDocument(new FileInputStream(template));
			Range range = doc.getRange();
			for (String key : params.keySet()) {
				range.replaceText(key, params.get(key).toString());
			}
			fos = new FileOutputStream(getRenameFile(WorkConstant.WORK_PATH, exportType, exportName));
			doc.write(fos);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					fos = null;
				}
			}
		}
	}
}

最后再看一下MainUtil怎么调用这两个工具类,增加了对template文件的检索,如果没有03的office,则搜索更高级的office文件。

FileType代码如下:

package excel2word.config.enumerations;

public enum FileType {
	XML_TYPE(".xml"),
	WORD_03_TYPE(".doc"),
	WORD_07UP_TYPE(".doc"),
	EXCEL_03_TYPE(".xls"),
	EXCEL_07UP_TYPE(".xls")
	;
	private String label;
	
	private FileType(String label) {
		this.label = label;
	}

	public String getLabel() {
		return label;
	}
}


MainUtil代码如下:

package excel2word;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import excel2word.config.constants.WorkConstant;
import excel2word.config.enumerations.FileType;
import excel2word.utils.DocUtil;
import excel2word.utils.XlsUtil;


public class MainUtil {
	
	public static void main(String[] args) {
		String xlsPath = WorkConstant.WORK_PATH+WorkConstant.TEMPLATE_NAME+FileType.EXCEL_03_TYPE.getLabel();
		String docPath = WorkConstant.WORK_PATH+WorkConstant.TEMPLATE_NAME+FileType.WORD_03_TYPE.getLabel();
		try {
			List<Map<String, Object>> xlsList = XlsUtil.extractXls(new FileInputStream(new File(xlsPath)));
			for (Map<String, Object> params : xlsList) {
				DocUtil.export(params, new File(docPath), FileType.WORD_03_TYPE, params.get("#{name}").toString());
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

代码看完了,有没有觉得之前的圈子绕的都太远了?

看来,思考确实比写代码更重要。


打JAR包:

在dajar包之前,最后做一些改变:将模板路径设置为当前执行目录,通过System.getProperty("user.dir")获取,并且要注意需要增加File.separator

WorkConstant代码如下:

package excel2word.config.constants;

import java.io.File;

public class WorkConstant {

	public static final String WORK_PATH = System.getProperty("user.dir") +File.separator;
	
}


删除了TEMPLATE_NAME 常量,希望通过扫描当前目录,检索出word和excel作为模板文件

需要检索出一个word和excel文件,不能多也不能少,而且可能是03or07+的格式,逻辑比较绕,没有想到较好的方式。

修改后的MainUtil代码如下:

package excel2word;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import excel2word.config.constants.WorkConstant;
import excel2word.config.enumerations.FileType;
import excel2word.utils.DocUtil;
import excel2word.utils.XlsUtil;


public class MainUtil {
	
	private static List<File> totalList = new ArrayList<File>();
	
	public static void main(String[] args) {
		Map<FileType, List<File>> param = new HashMap<FileType, List<File>>();
		for (FileType type : FileType.values()) {
			fillParam(totalList, param, type);
		}
		if (totalList.size() < 2) {
			System.out.println("找不到模板文件");
		} else if (2 == totalList.size() && 1 == (param.get(FileType.WORD_03_TYPE).size() + param.get(FileType.WORD_07UP_TYPE).size())) {
			dealParam(param);
		} else {
			for (File file : totalList) {
				System.out.println("无法鉴别模板文件");
				System.out.println(file.getName());
			}
		}
	}
	
	private static void dealParam(Map<FileType, List<File>> param) {
		FileType excelType = param.get(FileType.EXCEL_03_TYPE).size() > 0 ? FileType.EXCEL_03_TYPE : FileType.EXCEL_07UP_TYPE;
		FileType wordType = param.get(FileType.WORD_03_TYPE).size() > 0 ? FileType.WORD_03_TYPE : FileType.WORD_07UP_TYPE;
		File excelFile = param.get(excelType).get(0);
		File wordFile = param.get(wordType).get(0);
		try {
			List<Map<String, Object>> xlsList = XlsUtil.extractXls(new FileInputStream(excelFile));
			for (Map<String, Object> params : xlsList) {
				DocUtil.export(params, wordFile, wordType, params.get("#{name}").toString());
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			totalList.clear();
		}
	}

	private static void fillParam(List<File> totalList, Map<FileType, List<File>> param, FileType type) {
		param.put(type, scanFileBySuffix(totalList, type));
	}
	
	private static List<File> scanFileBySuffix(List<File> totalList, FileType suffix) {
		List<File> files = new ArrayList<File>();
		File dir = new File(WorkConstant.WORK_PATH);
		//考虑过files.size()>0即中止,返回List,可以把信息反馈给用户。
		if (dir.exists() && dir.isDirectory()){
			for (File file : dir.listFiles()) {
				if (!file.isDirectory() && file.getAbsolutePath().endsWith(suffix.getLabel())) {
					files.add(file);
				}
			}
		}
		totalList.addAll(files);
		return files;
	}
}


那么如何将我们的项目打成jar包,让别人可以通过命令行方式运行呢?

这里先介绍通过eclipse生成jar包的方式

第一步:右键单击项目,选择Export;


第二步:因为是要让他人可以通过命令行使用,所以选择Runnable JAR file


第三步:选择打包项目的Main方法类,指定输出路径,并且指定第三方lib的处理方式


有人可能不太理解Library handling的三种方式有什么区别,我来解释一下

Extract required libraries into generated JAR:提取出引用到的第三方JAR包的class,并打入项目生成的jar包中

Package required libraries into generated JAR:将引用到的第三方JAR包仍以JAR包形式放入生成的jar包中

Copy required libraries into a sub-folder next to the generated JAR:将引用到的第三方JAR包复制到上级目录的一个文件中。

翻译的很抽象?看一眼图就明白了


至于ant脚本,在我们介绍到ant的时候在介绍。

第一种方式,更方便查看第三方lib的源码(好吧,是我自己的理解),第二种则所占空间较少,第三种,额,可以很直观的知道自己的项目有多少自己的代码。。。

打包完成,我们来执行一下,命令行到jar包路径,执行

java -jar excel2word.jar

运行成功。

怎么通过命令行执行打jar包操作:

害羞(因为不会)。。。。。

怎么将jar打包成.exe?

附录:http://blog.csdn.net/luoweifu/article/details/7628006 大神介绍的很详细


也许你觉得这就可以了? 永远不要低估一颗程序员玩命折腾的心。。。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值