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

来源:互联网 发布:c语言源代码实例 编辑:程序博客网 时间:2024/05/29 16:58

既然使用的是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
原创粉丝点击