目前公司接的项目都有很多功能的数据都要依靠excel导入,因此对excel中数据的验证必不可少。
先来看一下目前存在的问题:
一:在每处导入的程序中都会包括excel读取、数据验证、错误数据的导出或错误的输出,每次都是拷贝、粘帖、修改,本来不同的地方只有验证部分,但由于格式、验证的不同,在修改过程中还要对excel的读取、错误的导出进行修改、调试,造成工作效率的降低和时间的浪费。
二:由于人员更替频繁,每个人的风格都不一样,对于错误的显示和输出都不一样,客户看到的结果是每处导入的地方返回的错误结果也不一样,有的只是提醒一句成功、失败,有的则会把错误的记录导出excel供客户下载修改。客户对此也有很多抱怨。
解决思路:
在excel导入中我们关心的(也是唯一不同的)是数据的验证、保存,对于读取、错误记录导出并不关心,那就该把这两部分分离出来,这样的好处有:1.导入时不再关心excel的读取和错误信息的导出,编码及调试时不再为这部分付出时间和精力,加快开发效率。2.降低耦合度,目前对excel的操作使用的是jxl,如果以后改为poi那只需要需改excel操作的实现即可。3.统一,所有的导入使用相同的excel操作实现,如果excel读取操作有bug则只需修改一处(写此代码的起因就是一个同事出现的一个bug引起的),而且对错误记录的输出也有统一的输出。
解决办法:
限于本人的表达能力,要想讲清楚太费时间和篇幅了,在这里就直接上代码了
首先是抽象类 ImportDataMultiSheet,包括excel读取、错误记录导出的实现,支持多sheet及合并单元格的处理
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.gaosheng.util.exception.EntityException;
import jxl.Cell;
import jxl.CellType;
import jxl.DateCell;
import jxl.NumberCell;
import jxl.Range;
import jxl.Sheet;
import jxl.Workbook;
import jxl.format.Colour;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
/**
* jxl导入excel类,继承类实现验证方法
*
* @author gaosheng
*
*/
public abstract class ImportDataMultiSheet {
private int startRow = 1;
private int startColumn = 0;
private int minRows = 1;
private int minColumns = 1;
private int maxRows = -1;
private int maxColumns = -1;
private Map<Integer, Method> methodMap;
private Map<Integer, List<String>> holdColumns;
private List<String>[] titiles ;
private List<MeregRange> meregRangeList ;
private Cell curCell;
private Cell[] curRowCells;
private int successcount = 0;
private String[] columnMethods = null;
private int[] needHoldColumns = null;
private File importExcel;
private File errorExcel;
private boolean hasError = false;
private List<String> errors = new ArrayList<String>();
/**
* 启动导入
*
* @return boolean
* @throws SecurityException
* @throws NoSuchMethodException
* @throws EntityException
*/
public boolean execute() throws SecurityException, NoSuchMethodException,
EntityException {
setMethodMap();
setHoldColumns();
Workbook work = null;
try {
work = Workbook.getWorkbook(importExcel);
} catch (Exception e) {
throw new EntityException("Excel表格读取异常!批量导入失败!<br/>");
}
//数据总行数
int totalRows = 0;
Sheet sheet = null;
WritableWorkbook writew = null;
WritableSheet writes = null;
int sheet_num = work.getNumberOfSheets();
// 全局验证
if (!this.validGlobal(work.getSheets())) {
throw new EntityException("导入文件格式错误");
}
try {
for (int sheet_index = 0; sheet_index < sheet_num ;sheet_index++) {
sheet = work.getSheet(sheet_index);
meregRangeList = new ArrayList<MeregRange>();
int columns = sheet.getColumns();
int rows = sheet.getRows();
totalRows += rows;
for (Range range : sheet.getMergedCells()) {
Cell topleft = range.getTopLeft();
Cell bottomRight = range.getBottomRight();
meregRangeList.add(new MeregRange(topleft.getRow(),topleft.getColumn(),bottomRight.getRow(),bottomRight.getColumn(),getCellValue(topleft)));
}
writew = Workbook.createWorkbook(errorExcel);
writes = writew.createSheet("ErrorReport", 0);
Label label;
WritableCellFormat wcf;
titiles = new List[startRow];
List<String> list = null;
for (int i = 0; i < startRow; i++) {
list = new ArrayList<String>();
for (int j = 0; j < columns; j++) {
label = new Label(j, i, getCellValue(sheet.getCell(j, i)));
writes.addCell(label);
list.add(getValue(sheet.getCell(j, i)));
}
titiles[i] = list;
}
label = new Label(columns, startRow - 1, "错误详细");
WritableFont wf0 = new WritableFont(WritableFont.TIMES, 12);
wcf = new WritableCellFormat(wf0);
label.setCellFormat(wcf);
writes.addCell(label);
int wi = startRow;
// -------------------------
StringBuffer info_temp = null;
String result = null;
Method method = null;
for (int i = startRow; i < rows; i++) {
curRowCells = sheet.getRow(i);
if (curRowCells == null || curRowCells.length < minColumns) {
continue;
}
boolean[] wj = new boolean[columns];
info_temp = new StringBuffer();
for (int j = startColumn; j < columns; j++) {
curCell = sheet.getCell(j, i);
// System.out.print(String.format("%-30.30s", this.getValue(curCell))+" ");
result = everyCell();
if (result != null) {
method = methodMap.get(j);
if (method == null) {
continue;
}
result = (String) method.invoke(this, null);
}
if (result != null) {
info_temp.append(result);
info_temp.append(" ");
wj[j] = true;
}
if (holdColumns.get(j) != null) {
holdColumns.get(j).add(this.getValue(curCell));
}
if (info_temp.length() > 0) {
errors.add("sheet "+sheet.getName()+" 中第 " + (i + 1) + " 行 :"
+ info_temp.toString());
}
}
// System.out.println();
if (info_temp.length() > 1) {
for (int ii = startColumn; ii < columns; ii++) {
Cell c_temp = sheet.getCell(ii, i);
label = new Label(ii, wi, c_temp.getContents().trim());
wcf = new WritableCellFormat();
if (wj[ii])
wcf.setBackground(Colour.RED);
label.setCellFormat(wcf);
writes.addCell(label);
}
label = new Label(columns, wi, info_temp.toString());
WritableFont wf = new WritableFont(WritableFont.TIMES,
12);
wf.setColour(Colour.RED);
wcf = new WritableCellFormat(wf);
label.setCellFormat(wcf);
writes.addCell(label);
wi++;
} else {
this.save();
successcount ++;
}
}
}
} catch (Exception e) {
e.printStackTrace();
this.hasError = true;
errors.add("sheet "+sheet.getName()+" 第"+this.curCell.getRow() +" 行 第 "+ this.curCell.getColumn()+" 列 :"+this.getCurCell().getContents()+" 遇到错误");
return false;
} finally {
try {
writew.write();
writew.close();
work.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (successcount < totalRows - sheet_num*startRow) {
this.hasError = true;
}
return true;
}
/**
* 全局验证,验证对行数和列数的要求
*
* @return
*/
public boolean validGlobal(Sheet[] sheets) {
for (int i = 0; i < sheets.length; i++) {
if (minRows != -1 && sheets[i].getRows() < minRows) {
return false;
} else if (minColumns != -1 && sheets[i].getColumns() < minColumns) {
return false;
} else if (maxRows != -1 && sheets[i].getRows() > maxRows) {
return false;
} else if (maxColumns != -1 && sheets[i].getColumns() > maxColumns) {
return false;
}
}
return true;
}
/**
* 一行数据验证成功后保存
* @return boolean
*/
public abstract boolean save();
/**
* 对每一个单元格进行的操作
* @return boolean
*/
public abstract String everyCell();
/**
* 初始化存储验证列方法的Map
*
* @throws SecurityException
* @throws NoSuchMethodException
*/
@SuppressWarnings("unchecked")
private void setMethodMap() throws SecurityException, NoSuchMethodException {
methodMap = new HashMap<Integer, Method>();
if (columnMethods == null) {
Method[] methods = this.getClass().getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().startsWith("validColumn_")) {
String column = methods[i].getName().substring(
methods[i].getName().indexOf("_") + 1);
try {
methodMap.put(Integer.parseInt(column), methods[i]);
} catch (Exception e) {
throw new NumberFormatException("默认列明必须为数字");
}
}
}
} else {
Class<ImportDataMultiSheet> class1 = (Class<ImportDataMultiSheet>) this.getClass();
for (int i = 0; i < columnMethods.length; i++) {
methodMap.put(i, class1.getMethod(columnMethods[i], null));
}
}
}
/**
* 初始化存储保留列的Map,保留列用于验证某些列值时需引用其他列的情况
*/
private void setHoldColumns() {
holdColumns = new HashMap<Integer, List<String>>();
if (needHoldColumns == null) {
return;
}
for (int i = 0; i < needHoldColumns.length; i++) {
holdColumns.put(needHoldColumns[i], new ArrayList<String>());
}
}
/**
* 获得给定单元格的实际值,对于时间会返回 'yyyy-MM-dd HH:mm:ss' 格式的字符串
*
* @param cell
* @return String
*/
public static String getCellValue(Cell cell) {
if (cell.getType().equals(CellType.NUMBER)) {
return Double.toString(((NumberCell) cell).getValue());
} else if (cell.getType().equals(CellType.DATE)) {
TimeZone gmt = TimeZone.getTimeZone("GMT");
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
Locale.getDefault());
dateFormat.setTimeZone(gmt);
return dateFormat.format(((DateCell) cell).getDate());
} else if (cell.getType().equals(CellType.EMPTY)) {
return null;
} else {
return cell.getContents().trim();
}
}
public String getValue(Cell cell){
String value = getCellValue(cell);
if (value == null || getCellValue(cell).equals("")) {
for(MeregRange meregRange:meregRangeList){
if (meregRange.isInRange(cell.getRow(), cell.getColumn())) {
return meregRange.getValue();
}
}
return value;
}else {
return value;
}
}
/**
* 防止空指针
*
* @param object
* @return String
*/
public String fixNull(Object object) {
return object == null ? "" : object.toString();
}
public int getMinRows() {
return minRows;
}
public void setMinRows(int minRows) {
this.minRows = minRows;
}
public int getMinColumns() {
return minColumns;
}
public void setMinColumns(int minColumns) {
this.minColumns = minColumns;
}
public int getMaxRows() {
return maxRows;
}
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
public int getMaxColumns() {
return maxColumns;
}
public void setMaxColumns(int maxColumns) {
this.maxColumns = maxColumns;
}
public String[] getColumnMethods() {
return columnMethods;
}
public void setColumnMethods(String[] columnMethods) {
this.columnMethods = columnMethods;
}
public File getImportExcel() {
return importExcel;
}
public void setImportExcel(File importExcel) {
this.importExcel = importExcel;
}
public File getErrorExcel() {
return errorExcel;
}
public void setErrorExcel(File errorExcel) {
this.errorExcel = errorExcel;
}
public boolean isHasError() {
return hasError;
}
public int[] getNeedHoldColumns() {
return needHoldColumns;
}
public void setNeedHoldColumns(int[] needHoldColumns) {
this.needHoldColumns = needHoldColumns;
}
public Map<Integer, List<String>> getHoldColumns() {
return holdColumns;
}
public int getStartRow() {
return startRow;
}
public void setStartRow(int startRow) {
this.startRow = startRow;
}
public int getStartColumn() {
return startColumn;
}
public void setStartColumn(int startColumn) {
this.startColumn = startColumn;
}
public Cell getCurCell() {
return curCell;
}
public List<String> getErrors() {
return errors;
}
public Cell[] getCurRowCells() {
return curRowCells;
}
public List<String>[] getTitiles() {
return titiles;
}
public int getSuccesscount() {
return successcount;
}
}
下面是一个实现类的范例:
import java.io.File;
import java.util.List;
import jxl.Cell;
import org.gaosheng.util.exception.EntityException;
import org.gaosheng.util.xls.ImportDataMultiSheet;
public class ImportDatemultiImp extends ImportDataMultiSheet {
public static void main(String[] args) throws SecurityException, NoSuchMethodException, EntityException {
File importFile = new File("F:/test.xls");
File errorFile = new File("F:/error.xls");
ImportDatemultiImp importDateImp = new ImportDatemultiImp();
importDateImp.setImportExcel(importFile);
importDateImp.setErrorExcel(errorFile);
importDateImp.setStartRow(1);
importDateImp.execute();
importDateImp.getErrorExcel();
for (String error : importDateImp.getErrors()) {
System.out.println(error);
}
}
//对每一个单元格的执行的统一操作,返回值为错误信息,没有错误则返回null
public String everyCell() {
Cell cell = this.getCurCell();
List<String> semList = this.getTitiles()[2];
List<String> courseList = this.getTitiles()[3];
if (cell.getRow() > 3 && cell.getColumn() > 3) {
String cellvalue = this.getValue(cell);
String course_name = courseList.get(cell.getColumn());
String reg_no = this.getValue(this.getCurRowCells()[1]);
String stuname = this.getValue(this.getCurRowCells()[2]);
if (cellvalue != null && !cellvalue.equals("") && course_name !=null && !course_name.equals("") && reg_no != null && !reg_no.equals("")) {
}else {
return "无效成绩";
}
}
return null;
}
//定义每一列的验证,默认方法名是validColumn_+列索引,也可以用setColumnMethods(String[] columnMethods)指定列的验证方法 返回值为错误信息,没有错误则返回null
public String validColumn_1(){
if (!this.getCurCell().getContents().equals("name")) {
return "姓名错误";
}
return null;
}
public String validColumn_2(){
if (!this.getCurCell().getContents().equals("passwd")) {
return "密码错误";
}
return null;
}
public String validColumn_3(){
return null;
}
//验证成功后保存记录
public boolean save() {
return false;
}
}