转载请注明来源:http://blog.csdn.net/loongshawn/article/details/51038051
- 《Java利用mpxj解析mpp格式文件》
- 《SpringBoot添加Email发送功能》
- 《SpringBoot配置log4j输出日志》
- 《SpringBoot定时任务说明》
- 《SpringBoot接口服务处理Whitelabel Error Page》
- 《构建基于阿里云OSS文件上传服务》
1、mpp文件介绍
MPP是Microsoft Project项目管理软件的文件扩展名,此软件旨在帮助个人跟踪,组织或维护项目。
2、mpp显示效果
可以通过在线的mpp阅读工具打开的,该工具免费,但限制了上传文件大小不超过2M。
https://www.projectplan365.com/projectviewernow/tViews.aspx#
3、mpp结构说明
通过上图可以看出,文件主体内容就是一条一条的记录,记录内容包括:任务ID、任务名、Duration、Start日期、Finish日期、Predecessors业务流、自定义字段。
接下来,我们先来了解下mpp文档的解析工具类,目前主要是通过MPXJ工具类来解析该文件。
MPXJ官方网站:http://www.mpxj.org/index.html
MPXJ官方API说明文档:http://www.mpxj.org/apidocs/index.html
实例中引入的maven依赖版本:
<dependency><groupId>net.sf.mpxj</groupId><artifactId>mpxj</artifactId><version>5.2.2</version>
</dependency>
Package net.sf.mpxj.mpp
上面这个包是处理MPP文件解析的主要集合,通过net.sf.mpxj.mpp.MPPReader类来构建读文件管道。
每条具体的任务都需要通过这个类net.sf.mpxj.Task来解析。
// 普通任务ID
Integer task_id =task.getID();
// 独立任务ID
Integer task_unique_id =task.getUniqueID();
// 大纲ID
Integer task_outline_level =task.getOutlineLevel();
// 任务周期
double task_duration =task.getDuration().getDuration();
// 任务名
String task_name = task.getName();
// 任务开始日期
Date task_start_date = task.getStart();
// 任务结束日期
Date task_finish_date = task.getFinish();
// 任务流
List<Relation> task_predecessors = task.getPredecessors();
4、mpp解析代码
解析线上mpp文件,同时获取TaskInfo
// NO.1 解析mpp文件,同时获取TaskInfopublic static List<TaskInfo> readInputStream(InputStream in,String fileName){List<TaskInfo> taskList = new ArrayList<TaskInfo>();InputStream ins = in;try{ MPPReader mppRead = new MPPReader();ProjectFile pf = mppRead.read(in);logger.info("MPXJUtils.method [readInputStream]: fileName-" + fileName);List<Task> tasks = pf.getAllTasks();logger.info("MPXJUtils.method [readInputStream]: taskSize-" + tasks.size());for (int i = 0; i < tasks.size(); i++) {Task task = tasks.get(i);Integer task_id = task.getID();Integer task_unique_id = task.getUniqueID();Integer task_outline_level = task.getOutlineLevel();double task_duration = task.getDuration().getDuration();String task_name = task.getName();Date task_start_date = task.getStart();Date task_finish_date = task.getFinish();List<Relation> task_predecessors = task.getPredecessors(); logger.info("MPXJUtils.method [readInputStream] taskInfo:" + task_id + "|" + task_unique_id + "|" + task_outline_level + "|" + task_duration + "|" + task_start_date + "|" + task_finish_date + "|" + task_predecessors);// 封装TaskInfojava.sql.Date sqlStartDate = Str2Date.getUKDate(task_start_date.toString()); // StartDate转换java.sql.Date sqlFinishDate = Str2Date.getUKDate(task_finish_date.toString()); // FinishDate转换StringBuffer sb = new StringBuffer();if(task_predecessors != null){if(task_predecessors.size() > 0){for(Relation relation : task_predecessors){Integer targetTaskId = relation.getTargetTask().getID();if(sb.length() == 0){sb.append(targetTaskId);}else{sb.append(","+targetTaskId);}}}}String task_predecessors_str = sb.toString(); // 任务流文本TaskInfo taskInfo = new TaskInfo();taskInfo.setTask_id(task_id);taskInfo.setTask_unique_id(task_unique_id);taskInfo.setTask_outline_level(task_outline_level);taskInfo.setTask_name(task_name);taskInfo.setTask_duration(task_duration);taskInfo.setTask_start_date(sqlStartDate);taskInfo.setTask_finish_date(sqlFinishDate);taskInfo.setTask_predecessors(task_predecessors_str);taskList.add(taskInfo); } }catch (MPXJException e) {logger.info("MPXJUtils.method [readInputStream]: MPXJException-" + e);return null; } catch (Exception e) { logger.info("MPXJUtils.method [readInputStream]: MPXJException-" + e);return null; } finally { try {ins.close();} catch (IOException e) {// TODO Auto-generated catch blocklogger.info("MPXJUtils.method [readInputStream]: IOException-" + e);return null; }}return taskList;}
封装过的TaskInfo
public class TaskInfo {private int project_id; // 所属项目IDprivate int task_id; // 任务IDprivate int task_unique_id; // 任务唯一IDprivate int parent_id; // 父任务IDprivate int task_outline_level; // 任务级别private String task_name; // 任务名称private double task_duration; // 任务工期private java.sql.Date task_start_date; // 任务开始时间private java.sql.Date task_finish_date; // 任务结束时间private String task_predecessors; // 任务流private String task_operator; // 负责人public int getProject_id() {return project_id;}public void setProject_id(int project_id) {this.project_id = project_id;}public int getTask_id() {return task_id;}public void setTask_id(int task_id) {this.task_id = task_id;}public int getTask_unique_id() {return task_unique_id;}public void setTask_unique_id(int task_unique_id) {this.task_unique_id = task_unique_id;}public int getParent_id() {return parent_id;}public void setParent_id(int parent_id) {this.parent_id = parent_id;}public int getTask_outline_level() {return task_outline_level;}public void setTask_outline_level(int task_outline_level) {this.task_outline_level = task_outline_level;}public double getTask_duration() {return task_duration;}public void setTask_duration(double task_duration) {this.task_duration = task_duration;}public Date getTask_start_date() {return task_start_date;}public void setTask_start_date(Date task_start_date) {this.task_start_date = task_start_date;}public Date getTask_finish_date() {return task_finish_date;}public void setTask_finish_date(Date task_finish_date) {this.task_finish_date = task_finish_date;}public String getTask_predecessors() {return task_predecessors;}public void setTask_predecessors(String task_predecessors) {this.task_predecessors = task_predecessors;}public String getTask_operator() {return task_operator;}public void setTask_operator(String task_operator) {this.task_operator = task_operator;}public String getTask_name() {return task_name;}public void setTask_name(String task_name) {this.task_name = task_name;}
}
解析本地mpp文件方法,供测试使用
public static List<TaskInfo> readFile(){List<TaskInfo> taskList = new ArrayList<TaskInfo>();try{ File file = new File("/Users/ffff/Downloads/计划(含月度版)V0.10-20160222.mpp");MPPReader mppRead = new MPPReader();ProjectFile pf = mppRead.read(file);logger.info("MPXJUtils.method [readFile]: fileName-" + file.getName());List<Task> tasks = pf.getAllTasks();logger.info("MPXJUtils.method [readFile]: taskSize-" + tasks.size());for (int i = 0; i < tasks.size(); i++) {Task task = tasks.get(i);Integer task_id = task.getID();Integer task_unique_id = task.getUniqueID();Integer task_outline_level = task.getOutlineLevel();double task_duration = task.getDuration().getDuration();Date task_start_date = task.getStart();Date task_finish_date = task.getFinish();List<Relation> task_predecessors = task.getPredecessors(); logger.info("MPXJUtils.method [readFile] taskInfo:" + task_id + "|" + task_unique_id + "|" + task_outline_level + "|" + task_duration + "|" + task_start_date + "|" + task_finish_date + "|" + task_predecessors);// 封装TaskInfojava.sql.Date sqlStartDate = Str2Date.getUKDate(task_start_date.toString()); // StartDate转换java.sql.Date sqlFinishDate = Str2Date.getUKDate(task_finish_date.toString()); // FinishDate转换StringBuffer sb = new StringBuffer();if(task_predecessors != null){if(task_predecessors.size() > 0){for(Relation relation : task_predecessors){Integer targetTaskId = relation.getTargetTask().getID();if(sb.length() == 0){sb.append(targetTaskId);}else{sb.append(","+targetTaskId);}}}}String task_predecessors_str = sb.toString(); // 任务流文本TaskInfo taskInfo = new TaskInfo();taskInfo.setTask_id(task_id);taskInfo.setTask_unique_id(task_unique_id);taskInfo.setTask_outline_level(task_outline_level);taskInfo.setTask_duration(task_duration);taskInfo.setTask_start_date(sqlStartDate);taskInfo.setTask_finish_date(sqlFinishDate);taskInfo.setTask_predecessors(task_predecessors_str);taskList.add(taskInfo); } }catch (MPXJException e) {logger.info("MPXJUtils.method [readFile]: MPXJException-" + e);return null; } catch (Exception e) { logger.info("MPXJUtils.method [readFile]: MPXJException-" + e);return null; } return taskList;}
本地测试,输出mpp文件结果:
获取子任务间的所属父子关系
// NO.2 获取TaskInfo之间的父子关联关系public static List<TaskInfo> refreshTaskInfo(List<TaskInfo> taskList){List<Map<String,Integer>> tempTaskOutLine = new ArrayList<Map<String,Integer>>();for(TaskInfo taskInfo : taskList){int taskId = taskInfo.getTask_id();int taskOutLineLevel = taskInfo.getTask_outline_level(); int listSize = tempTaskOutLine.size();logger.info("MPXJUtils.method [refreshTaskInfo1]: taskId-" + taskId + ",taskOutLineLevel-" + taskOutLineLevel + ",listSize-" + listSize);// 初始化taskOutLineLevelif(listSize > 2){ if(taskOutLineLevel == 1){ for(int i=listSize;i>2;i--){tempTaskOutLine.remove(i-1);}listSize = 2; logger.info("MPXJUtils.method [refreshTaskInfo2]: taskId-" + taskId + ",taskOutLineLevel-" + taskOutLineLevel + ",listSize-" + listSize);} }Map<String,Integer> map = new HashMap<String,Integer>();map.put("taskId", taskId);map.put("taskOutLineLevel", taskOutLineLevel);if(listSize == 0){if(taskOutLineLevel == 0){tempTaskOutLine.add(map);}else{return null;}}else{Map<String,Integer> lastMap = tempTaskOutLine.get(listSize-1);int lastTaskId = lastMap.get("taskId");int lastTaskOutLineLevel = lastMap.get("taskOutLineLevel");if(taskOutLineLevel > lastTaskOutLineLevel){tempTaskOutLine.add(map);taskInfo.setParent_id(lastTaskId);}else if(taskOutLineLevel == lastTaskOutLineLevel){ tempTaskOutLine.set(taskOutLineLevel, map);Map<String,Integer> lastMap1 = tempTaskOutLine.get(taskOutLineLevel-1);int lastTaskId1 = lastMap1.get("taskId");taskInfo.setParent_id(lastTaskId1);}else if(taskOutLineLevel < lastTaskOutLineLevel){ tempTaskOutLine.set(taskOutLineLevel, map);Map<String,Integer> lastMap2 = tempTaskOutLine.get(taskOutLineLevel-1);int lastTaskId2 = lastMap2.get("taskId");taskInfo.setParent_id(lastTaskId2);}} }return taskList;}
5、数据存储
将解析的结果,经过子任务排序,即获取每条子任务的父任务,大家可以理解为书本目录的大纲。
下面看看数据存储到数据库后的效果:
6、自定义字段读取
有时候用户会自定义字段来使事件描述得更加清晰,那如何读取自定义字段呢?
首先,需要知道这个自定义字段的Index,如何查看,我以网页编辑器为例:
双击负责人这一列,会弹出下列窗口:
弹出窗口:
上面这个弹出窗口里面有如下标识:负责人 ( Text2 ),其中Text2标识表示这个字段存在Index为2的Text里面。因此可以通过如下方法读取内容:
String task_operator = task.getText(2);
读取结果:
最后,我说说我这边没有读取自定义字段的原因,当前我们这边生产mpp文件不规范,不同人操作可能造成自定义字段下标不一致,经过验证也确实存在不一致的情况,这种随机的情况很容易造成错误,所以在没有规范流程前尽量不要去读取自定义字段。
若有同学在实际工作中发现mpxj解析mpp还存在其他的问题,请不吝赐教,谢谢!