初衷:项目中一直没有一个好用的Excel导入导出功能。所以简单实现了导入导出功能分享大家一起讨论和学习
commons-beanutils
commons-beanutils
1.8.0
org.apache.poi
poi-ooxml
3.9
net.sf.jxls
jxls-core
1.0.6
net.sf.jxls
jxls-reader
1.0.6
org.apache.ant
ant
1.9.4
ExcelUtil.java
/**
* Excel导入
*
* @author http://blog.csdn.net/make_a_difference
*/
@SuppressWarnings("unchecked")
public class ExcelUtil {
private static Log logger = LogFactory.getLog(ExcelUtil.class);
// 换行标识
private final static String ENTER_STR = "\n";
/**
* 默认解析Excel第一个sheet页
*
* @param is
* 输入流
* @param startRow
* 开始解析行
* @param clazz
* 实体类
* @return
* @throws ExcelException
*/
public static List excelParsing(InputStream is, Integer startRow, Class clazz) throws ExcelException {
return excelParsing(is, 1, startRow, clazz);
}
/**
* 默认解析Excel第一个sheet页
*
* @param is
* 输入流
* @param startRow
* 开始解析行
* @param clazz
* 实体类
* @return
* @throws ExcelException
* @throws FileNotFoundException
*/
public static List excelParsing(File file, Integer startRow, Class clazz) throws ExcelException {
FileInputStream is;
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e) {
logger.error("Excel文件异常", e);
throw new ExcelException("Excel文件异常");
}
return excelParsing(is, 1, startRow, clazz);
}
/**
* 解析Excel
*
* @param is
* 输入流
* @param sheetIndex
* sheet页数
* @param startRow
* 开始解析行
* @param clazz
* 实体类
* @return
* @throws ExcelException
*/
public static List excelParsing(InputStream is, Integer sheetIndex, Integer startRow, Class clazz)
throws ExcelException {
// 1: 获取工作簿
Workbook wb = null;
try {
wb = ExcelUtil.getWorkbook(is);
} catch (Exception e) {
logger.error("系统解析Excel获取WorkBook错误", e);
throw new ExcelException("系统解析Excel错误");
}
// 2:获取第一个sheet页码
Sheet sheet = wb.getSheetAt(sheetIndex - 1);
return doParsing(sheet, startRow, clazz);
}
@SuppressWarnings("rawtypes")
private static List doParsing(Sheet sheet, Integer startRow, Class clazz) throws ExcelException {
StringBuffer errorMessage = new StringBuffer("");
// 1:获取sheet页的总行数
int lastRow = sheet.getLastRowNum();
// 2: 行解析,默认从0开始,startRow属性定义了数据从该行开始解析,程序中定义的行数从1开始算,所以此处要-1
List resultList = new ArrayList(3);
try {
for (int rowIndex = startRow - 1; rowIndex <= lastRow; rowIndex++) {
Row row = sheet.getRow(rowIndex); // 得到 第 n 行
// 2.1获取的列为空则跳出循环
if (row == null) {
continue;
}
// 2.2 解析对象
Object rowBean = clazz.newInstance();
String rowErrorMessage = check(row, rowIndex, rowBean);
// 2.3 如果返回的不是null,则存在错误信息则记录错误信息
if (rowErrorMessage != null) {
errorMessage.append(rowErrorMessage + ENTER_STR);
continue;
} else if ("end".equals(rowErrorMessage)) {
resultList.add(rowBean);
return resultList;
}
// 2.4 如果正确 则不读取
resultList.add(rowBean);
}
} catch (Exception e) {
logger.error("系统解析Excel列错误", e);
throw new ExcelException("系统解析Excel错误");
}
// 3:如果存在错误则需要将异常抛出
if (errorMessage.length() > 0) {
throw new ExcelException(errorMessage.toString());
}
return resultList;
}
/**
*
* @param row
* 行信息
* @param rowIndex
* 当前行数
* @param declaredFields
* @param rowBean
* @return 错误信息
* @throws Exception
*/
private static String check(Row row, int rowIndex, Object rowBean) throws Exception {
// 1: 对象中循环取数据并赋值
for (Field field : rowBean.getClass().getDeclaredFields()) {
// 2:包含excel规则校验的注解
ExcelFieldMeta meta = field.getAnnotation(ExcelFieldMeta.class);
// 3:单元格的值
Cell cell = row.getCell(meta.cell() - 1);
String cellValue = getCellValue(cell);
Object obejctVal = null;
// 3.1 第一个单元并单元格值为空则表示读取excel结束
if (meta.cell() == 1 && (cell == null || "".equals(cellValue) || "null".equals(cellValue)))
return "end";
// 4.2校验单元格是否必填,单元格可以为空
if (!meta.isNotNull() && (cellValue == null || "".equals(cellValue))) {
continue;
}
// 4.2:可以为空且为空
if (meta.isNotNull() && (cellValue == null || "".equals(cellValue))) {
return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容不能为空值";
}
// 4.3 整型、浮点型
if (field.getType() == Integer.class) {
try {
// 此处采用浮点数转换,验证value中是否含有字符。整形转换时只要整数部分的数据
obejctVal = (int) cell.getNumericCellValue();
} catch (Exception ne) {
return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容只能为数值,读取时为:" + cellValue;
}
} else if (field.getType() == Double.class) {// 4.4 浮点型
try {
// 此处采用浮点数转换,验证value中是否含有字符。整形转换时只要整数部分的数据
obejctVal = cell.getNumericCellValue();
} catch (NumberFormatException ne) {
return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容只能为数值,读取时为:" + cellValue;
}
} else if (field.getType() == Date.class) {// 4.5处理日期类型
SimpleDateFormat sf = new SimpleDateFormat(meta.dateFormat());
try {
obejctVal = sf.parse(cellValue);
obejctVal.toString();
} catch (ParseException e) {
return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容中日期格式异常,读取时为" + cellValue;
}
} else if (field.getType() == String.class) {
obejctVal = cellValue;
}
// 4.6 长度校验
if (meta.maxLength() != -1 && cellValue.length() > meta.maxLength()) {
return "[" + (rowIndex + 1) + "]行-[" + (meta.cell()) + "]列:此单元格内容的长度不能超出" + meta.maxLength() + "个字符";
}
// 4.7设置属性
PropertyUtils.setProperty(rowBean, field.getName(), obejctVal);
}
return null;
}
/**
* 得到当前列的值
*
* @param cell
* @return
*/
private static String getCellValue(Cell cell) {
String typeString = "";
if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
typeString = String.valueOf(cell.getNumericCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
typeString = String.valueOf(cell.getStringCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
typeString = String.valueOf(cell.getDateCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_BLANK) {
typeString = String.valueOf(cell.getStringCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_ERROR) {
typeString = "";
}
return typeString;
}
/**
* 解析不同版本的Excel文件
*
* @param in
* @return
* @throws IOException
* @throws InvalidFormatException
*/
private static Workbook getWorkbook(InputStream in) throws IOException, InvalidFormatException {
if (!in.markSupported()) {
in = new PushbackInputStream(in, 8);
}
if (POIFSFileSystem.hasPOIFSHeader(in)) {
return new HSSFWorkbook(in);
}
if (POIXMLDocument.hasOOXMLHeader(in)) {
try {
return new XSSFWorkbook(OPCPackage.open(in));
} catch (InvalidFormatException e) {
logger.error("Excel文件异常", e);
}
}
throw new IllegalArgumentException("你的excel版本目前poi不支持");
}
ExcelFieldMeta.java
/**
* Excel对象自定义注解
*
* @author http://blog.csdn.net/make_a_difference
*
*/
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ ElementType.FIELD, ElementType.METHOD }) // 定义注解的作用目标**作用范围字段、枚举的常量/方法
@Documented // 说明该注解将被包含在javadoc中
public @interface ExcelFieldMeta {
// 对应Excel文档中的列号
int cell() default 0;
// 不能为空,默认为 是
boolean isNotNull() default true;
// 最大长度, -1 则不限制
int maxLength() default -1;
// 日期格式,默认导入格式为"yyyy/MM/dd"如"2015/5/13"
String dateFormat() default "yyyy/MM/dd";
}
ExcelException.java
/**
* Excel处理异常
*
* @author http://blog.csdn.net/make_a_difference
*/
public class ExcelException extends Exception {
/**
* @Fields serialVersionUID: (用一句话描述这个变量表示什么)
*/
private static final long serialVersionUID = 1L;
public ExcelException() {
super();
}
public ExcelException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public ExcelException(String message, Throwable cause) {
super(message, cause);
}
public ExcelException(String message) {
super(message);
}
public ExcelException(Throwable cause) {
super(cause);
}
}
/**
* Excel下载
*
* @author http://blog.csdn.net/make_a_difference
*/
public class ExcelExport {
/**
* 下载excel文件,内容使用MAP存放 。 支持多个sheet。两个map的长度必须一致,map的key为索引(index)
*
* @param response
* 响应流
* @param headName
* Excel表名
* @param tableHeadMap
* 每个sheet对应的表头为具体的value值
* @param tableBodyMap
* 每个sheet对应的内容为具体的value值
*/
public static void downloadExcelMap(HttpServletResponse response, String headName,
Map> tableHeadMap, Map>> tableBodyMap)
throws Exception {
headName = replaceAllSpecial(headName);
// 1:创建一个workbook
Workbook workbook = new HSSFWorkbook();
// 创建样式
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBoldweight(Font.BOLDWEIGHT_BOLD); // 粗体
style.setFont(font);
style.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平居中
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
style.setBorderTop((short) 1);
style.setBorderBottom((short) 1);
style.setBorderLeft((short) 1);
style.setBorderRight((short) 1);
// 设置合计样式
CellStyle style1 = workbook.createCellStyle();
style1.setAlignment(HSSFCellStyle.ALIGN_CENTER); // 水平居中
style1.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER); // 垂直居中
style1.setBorderTop((short) 1);
style1.setBorderBottom((short) 1);
style1.setBorderLeft((short) 1);
style1.setBorderRight((short) 1);
int sheetLength = tableHeadMap.size();
if (sheetLength != tableBodyMap.size()) {
throw new Exception("tableHeadMap与tableBodyMap对应的长度不一致");
}
for (int i = 0; i < sheetLength; i++) {
Sheet sheet = workbook.createSheet(headName + (i + 1));
List tableHead = tableHeadMap.get(i);
List
/**
* Excel 模板下载
*
* @author http://blog.csdn.net/make_a_difference
*/
public class ExcelTempletExport {
/**
* 通过模板下载Excel文件
* ${bean.courseName}
*
* @param templateUrl
* 模板路径 /org/gtiles/components/gtclasses/workbench/
* teacherfacecoursecount/list/templateTeacherCourse.xlsx
* @param tableBody
* 输出内容list
* @param fileName
* 文件名称
* @param response
* 相应流
*
* @throws Exception
*/
public static void downloadExcel(String templatePath, List> tableBody, String fileName,
HttpServletResponse response) throws Exception {
Map beanParams = new HashMap();
beanParams.put("list", tableBody);
downloadExcel(templatePath, beanParams, fileName, response);
}
/**
* 通过模板下载Excel文件
* ${bean.courseName}
*
* @param templateUrl
* 模板路径 /org/gtiles/components/gtclasses/workbench/
* teacherfacecoursecount/list/templateTeacherCourse.xlsx
* @param map
* 输出内容 key-value
* @param fileName
* 文件名称
* @param response
* 相应流
*
* @throws Exception
*/
public static void downloadExcel(String templatePath, Map map, String fileName,
HttpServletResponse response) throws Exception {
// 1: 创建XLSTransformer对象
XLSTransformer transformer = new XLSTransformer();
// 2:获取模板,读取jar文件并返回流
InputStream is = ExcelTempletExport.class.getResourceAsStream(templatePath);
try {
// 4:设置响应头
response.setHeader("Content-Disposition",
"attachment;filename=\"" + new String(fileName.getBytes("gb2312"), "iso8859-1") + ".xlsx\"");
response.setContentType("application/vnd.ms-excel");
// 5:通过流向客户端写数据
transformer.transformXLS(is, map).write(response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null)
is.close();
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
/**
* 通过模板将Excel写入到另外一个Excel文件中
*
* @param srcFilePath
* 模板路径
* @param tableBody
* 输出内容
* @param destFilePath
* 输出目标文件
* @throws Exception
*/
public static void downloadExcel(String srcFilePath, List> tableBody, String destFilePath) throws Exception {
// 1: 创建XLSTransformer对象
XLSTransformer transformer = new XLSTransformer();
// 2:将数据转换成Map格式
Map beanParams = new HashMap();
beanParams.put("list", tableBody);
// 3:写出文件
transformer.transformXLS(srcFilePath, beanParams, destFilePath);
}
}
/**
* test
* @author http://blog.csdn.net/make_a_difference
*/
public class TestBean {
@ExcelFieldMeta(cell = 1)
private String name;
@ExcelFieldMeta(cell = 2, isNotNull = false)
private Integer age;
@ExcelFieldMeta(cell = 3)
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public static void main(String[] args) throws FileNotFoundException {
try {
// List excelParsing = ExcelUtil.excelParsing(new File("C:\\Users\\Administrator\\Desktop\\test.xlsx"), 1, TestBean.class);
//1:导入
List excelParsing = ExcelUtil.excelParsing(new FileInputStream("C:\\Users\\Administrator\\Desktop\\test.xlsx"), 1, 1, TestBean.class);
//2:导出
ExcelTempletExport.downloadExcel("模板路径",excelParsing, "文件名", response);
} catch (ExcelException e) {
System.out.println(e.toString());//excel解析的错误。例:[2]行-[2]列:此单元格内容只能为数值,读取时为:qx
}
}
Excel使用jxls模板导出