引言
在公司写代码,常常逃不过数据的统计,但是操作Excel又很烦恼,于是就手写了一个操作便捷的工具类。
有几个功能亮点
方法详细参数请参考下面源代码。
1. createExcel()
- 直接通过传入类型内容数据
List<List<String>>
和标题String []title
就能生成一张简介的excel表格。
2. objectListProToStrList()
- 但是上面的内容数据又不好封装,常常会从实体类中提取,于是就有了FieldUtils这个工具类,专门通过反射操作对象属性用的。
3. getCellValue()
- 传入Cell,直接自动判断类型返回字符串,时间格式还可以自定义。
下面放出临时代码,以后的excel工具代码都在github中更新,github地址。下面的代码已经不是最新的了,需要最新的代码去github的utils里面Copy,都有注释的 Joy。
ExcelUtils.java
/**
* Create by MoonFollow (or named FireLang)
* Only For You , Joy
* Date: 2017/10/17
* Time: 12:45
* Excel工具类:所有方法都经过深思熟虑实现,扩展强,功能实现具体,BUG 少,结构堪称完美!!!
*
* 都是为了让你写出优雅的代码,不!是少加班!
*/
public class ExcelUtils {
private static SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
/**
*
* @param bathPath 要类似于这样的路径:c:/base/path/
* @param fileName 随意
* @param insertData 字符串List
* @param sheetName 要创建的sheetName
* @param titleName 标题名数组
* @param startX 整个表的起始位置X
* @param startY 整个表的起始位置Y
* @throws Exception 异常
*/
public static void createExcel(String bathPath, String fileName, List<List<String>> insertData, String sheetName, String titleName[], int startX, int startY) throws Exception {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet(sheetName);//创建表
CellStyle cellStyle = wb.createCellStyle();
cellStyle.setBorderTop(BorderStyle.THIN);//设置标题细黑色边框
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setAlignment(HorizontalAlignment.CENTER);//让文本居中
Row titleRow = sheet.createRow(startY);//创建标题行
//设置标题行内容
for (int i = 0; i < titleName.length; i++) {
String tempTitle = titleName[i];
if(tempTitle == null){
continue;
}
int startIndexColumn = i+startX;//计算开始插入列
Cell tempCell = titleRow.createCell(startIndexColumn);
tempCell.setCellValue(tempTitle);
tempCell.setCellStyle(cellStyle);
//计算列宽并且设置列宽,按照最大值设置
int maxStrLen = getMaxLengthInColumn(insertData,i);
maxStrLen = maxStrLen < tempTitle.length() ? tempTitle.length() : maxStrLen;
System.out.println(maxStrLen);
sheet.setColumnWidth(startIndexColumn,520*maxStrLen);
}
int allCellCount = titleName.length;//一共有多少列,列按照标题来
int startRowIndex = startY+1;//内容的起始行
int allRow = insertData.size();//内容一共有多少行
CellStyle tempCellStyle = wb.createCellStyle();
tempCellStyle.setAlignment(HorizontalAlignment.CENTER);//让文本居中
for (int row = 0; row < allRow; row++) {//遍历所有的内容
Row tempRow = sheet.createRow(row+startRowIndex);
List<String> tempData = insertData.get(row);
int nowCellCount = tempData.size();//实际的列数
for (int cell = 0; cell < allCellCount; cell++) {
if(nowCellCount<=cell){//如果当前遍历的列数大于实际的列数就退出当前行,设置下一行!
break;
}
Cell targetCell = tempRow.createCell(cell+startX);//创建列,设置列。
targetCell.setCellStyle(tempCellStyle);//让文本居中
targetCell.setCellValue(tempData.get(cell));
}
}
try {
OutputStream outputStream = new FileOutputStream(bathPath+fileName);
wb.write(outputStream);
outputStream.close();
wb.close();
} catch (Exception e) {
throw new BaseException(StaticString.WARNING_TYPE,"生成的excel写入文件失败!");
}
}
/**
* 在excel内容数据中找到当前列的最长字符串长度
* @param data excel内容数据
* @param column 指定列
* @return 当前列最长字符串长度
*/
public static int getMaxLengthInColumn(List<List<String>> data ,int column){
int max = 0;
for (List<String> tempData : data) {
if(tempData.size() <= column){
continue;
}
String tempStr = tempData.get(column);
if(tempStr == null){
continue;
}
int tempLen = tempStr.length();
max = max < tempLen ? tempLen : max;
}
return max;
}
/**
* 如果Cell为时间时,就通过默认的时间格式获取值
* @param cell 目标Cell
* @return 返回目标Cell字符串
*/
public static String getCellValue(Cell cell){
return getCellValue(cell,format);
}
/**
* 获取Cell的值,自动判断类型
* @param cell 目标Cell
* @return 返回字符串
*/
public static String getCellValue(Cell cell,SimpleDateFormat format) {
if(cell == null){//如果为空就返回Null
return null;
}
String o = null;
int cellType = cell.getCellType();//获取当前Cell的类型
switch (cellType) {
//这里有个技巧,就是只判断特殊的几种类型,其它的类型就直接通过default处理。
case Cell.CELL_TYPE_ERROR:// 5 当该单元格数据 ERROR 的时候,(故障)
break;
case Cell.CELL_TYPE_BLANK:// 3 当该单元格没有数据的时候
o = "";
break;
case Cell.CELL_TYPE_NUMERIC:// 0 当该单元格数据为数字的时候
o = getValueOfNumericCell(cell,format);
break;
case Cell.CELL_TYPE_FORMULA:// 2 当该单元格数据为公式的时候
try {
o = getValueOfNumericCell(cell,format);
} catch (IllegalStateException e) {
o = cell.getRichStringCellValue().toString();
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
o = cell.getRichStringCellValue().toString();
}
return o;
}
/**
* 获取日期类型Cell的字符串值,通过默认的日期Pattern
* @param cell 目标Cell
* @return 返回目标Cell字符串
*/
private static String getValueOfNumericCell(Cell cell) {
return getValueOfNumericCell(cell,format);
}
/**
* 获取日期类型Cell的字符串值
* @param cell 目标Cell
* @param format 日期Pattern
* @return 返回目标Cell字符串
*/
private static String getValueOfNumericCell(Cell cell, SimpleDateFormat format) {
Boolean isDate = DateUtil.isCellDateFormatted(cell);
String o = null;
if (isDate) {
o = format.format(cell.getDateCellValue());
} else {
o = String.valueOf(cell.getNumericCellValue());
}
return o;
}
}
FieldUtils.java
/**
* Create by MoonFollow (or named FireLang)
* Only For You , Joy
* Date: 2017/10/17
* Time: 12:57
* 属性工具类,所有方法都经过深思熟虑实现,扩展强,功能实现具体,BUG 少,结构堪称完美!!!
*
* 都是为了让你写出优雅的代码,不!是少加班!
*/
public class FieldUtils {
/**
* 在指定Class对象里面获取指定的属性
* @param fieldName 属性名
* @param targetClazz Class对象
* @return
*/
public static Field getTargetField(String fieldName, Class targetClazz){
Field result = null;
while (targetClazz != null){
try {
result = targetClazz.getDeclaredField(fieldName);
return result;
} catch (NoSuchFieldException e) {
targetClazz = targetClazz.getSuperclass();
}
}
return result;
}
/**
* 将 List<Object> 中的属性按照属性数组顺序提取城 List<List<String>>,注意:使用该方法的前提是,你要提取的属性值都是字符串!
* @param insertData 被转换的数据源
* @param objectProName 数据属性
* @return
*/
public static List<List<String>> objectListProToStrList(List<?> insertData, String [] objectProName){
List<List<String>> result = new LinkedList<>();
for (Object tempInsert : insertData) {
List<String> tempDate = objectProToStrList(tempInsert,objectProName);
result.add(tempDate);
}
return result;
}
/**
* 提取指定对象里面的指定属性,并且按照指定属相数组的顺序提取。注意:使用该方法的前提是,你要提取的属性值都是字符串!
* @param data 指定对象
* @param objectProName 指定属性字符串数组
* @return 指定提取的属性List
*/
public static List<String> objectProToStrList(Object data , String []objectProName){
List<String> tempItem = new LinkedList<>();//创建装指定属性值的容器
Class tempClazz = data.getClass();//获得该对象的Class对象
for (String tempPro : objectProName) {
try {
Field tempField = FieldUtils.getTargetField(tempPro,tempClazz);
tempField.setAccessible(true);//设置当前属性可访问
String tempProValue = (String) tempField.get(data);
if(tempProValue == null){//如果当前属性为 Null 那么就输出空字符串。
tempProValue = "";
}
tempItem.add(tempProValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return tempItem;
}
}
BaseException.java
public class BaseException extends Exception{
private String errorType;//这是错误类型,一般是前端会用的一个东西,它标志着 key 。
private String errorMessage;//这是错误信息,代表具体错误解释。
public BaseException(String errorType, String errorMessage) {
this.errorType = errorType;
this.errorMessage = errorMessage;
}
public BaseException() {
}
public BaseException(String message) {
super(message);
}
public BaseException(String message, Throwable cause) {
super(message, cause);
}
public BaseException(Throwable cause) {
super(cause);
}
}
StaticString.java
public class StaticString {
public static final String DEFAULTPATH = "K:\\文件上传\\POI学习测试数据\\";
//错误级别。以后自定义错误的时候,建议通过错误级别分类。这是血的教训!!!
public static final String WARNING_TYPE = "WARNING";
public static final String DANGER_TYPE = "DANGER";
}