Excel 数据导入和导出工具使用手册
这篇文档将详细介绍如何使用 Excel 数据导入和导出工具来有效地处理 Excel 文件的导入和导出操作。这些工具包括三个主要类:ExcelExportUtil
、ExcelImportUtil
和 ExcelAttribute
。
ExcelAttribute - 定义 Excel 列属性
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelAttribute {
/** 对应的列名称 */
String name() default "";
/** Excel 列的索引 */
int sort();
/** 字段类型对应的格式 */
String format() default "";
}
ExcelExportUtil - 导出数据
ExcelExportUtil 类
java
import com.mohr.domain.poi.ExcelAttribute;
import lombok.Getter;
import lombok.Setter;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 导出 Excel 工具类
* 基于模板的方式导出:
*/
@Getter
@Setter
public class ExcelExportUtil<T> {
private int rowIndex; // 写入数据的起始行
private int styleIndex; // 样式的行号
private Class clazz; // 对象的字节码
private Field fields[]; // 对象中的所有属性
public ExcelExportUtil(Class clazz, int rowIndex, int styleIndex) {
this.clazz = clazz;
this.rowIndex = rowIndex;
this.styleIndex = styleIndex;
fields = clazz.getDeclaredFields();
}
/**
* 基于注解导出
*
* @param response HTTP 响应对象
* @param is 模板的输入流
* @param objs 数据列表
* @param fileName 生成的文件名
* @throws Exception 可能的异常
*/
public void export(HttpServletResponse response, InputStream is, List<T> objs, String fileName) throws Exception {
XSSFWorkbook workbook = new XSSFWorkbook(is); // 创建工作簿
Sheet sheet = workbook.getSheetAt(0); // 读取工作表
CellStyle[] styles = getTemplateStyles(sheet.getRow(styleIndex)); // 提取样式
AtomicInteger datasAi = new AtomicInteger(rowIndex); // 行索引
for (T t : objs) {
Row row = sheet.createRow(datasAi.getAndIncrement()); // 创建行
for (int i = 0; i < styles.length; i++) {
Cell cell = row.createCell(i);
cell.setCellStyle(styles[i]);
for (Field field : fields) {
if (field.isAnnotationPresent(ExcelAttribute.class)) {
field.setAccessible(true);
ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class);
if (i == ea.sort()) {
if (field.get(t) != null) {
cell.setCellValue(field.get(t).toString());
}
}
}
}
}
}
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/octet-stream");
response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("ISO8859-1")));
response.setHeader("filename", fileName);
workbook.write(response.getOutputStream());
}
public CellStyle[] getTemplateStyles(Row row) {
CellStyle[] styles = new CellStyle[row.getLastCellNum()];
for (int i = 0; i < row.getLastCellNum(); i++) {
styles[i] = row.getCell(i).getCellStyle();
}
return styles;
}
}
步骤 1: 准备数据
- 准备需要导出的数据列表。
- 确保数据对象的属性与
ExcelAttribute
注解相匹配,包括列名、排序索引和格式。
步骤 2: 创建 Excel 模板文件
- 创建一个 Excel 模板文件,包含用于设置样式的行和用于填充数据的占位符单元格。
- 确保模板文件的格式与数据对象属性匹配。
步骤 3: 调用导出方法
java
// 示例代码
List<EmployeeReportResult> results = userCompanyPersonalMapper.findByReport(companyId, month + "%");
InputStream fis = new FileInputStream("path/to/your/template.xlsx");
new ExcelExportUtil(EmployeeReportResult.class, 2, 2)
.export(response, fis, results, "员工报表.xlsx");
- 创建
ExcelExportUtil
对象,传入数据对象的类、数据起始行索引和样式行索引。 - 获取模板文件的输入流。
- 调用
export
方法,传入HttpServletResponse
对象、模板文件的输入流、数据列表和生成的文件名。
ExcelImportUtil - 导入数据
ExcelImportUtil 类
java
import com.mohr.domain.poi.ExcelAttribute;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class ExcelImportUtil<T> {
private Class clazz;
private Field fields[];
public ExcelImportUtil(Class clazz) {
this.clazz = clazz;
fields = clazz.getDeclaredFields();
}
/**
* 从Excel文件中读取数据并映射到对象列表
*
* @param is Excel文件的输入流
* @param rowIndex 数据起始行索引
* @param cellIndex 数据起始列索引
* @return 包含映射数据的对象列表
*/
public List<T> readExcel(InputStream is, int rowIndex, int cellIndex) {
List<T> list = new ArrayList<T>();
T entity = null;
try {
XSSFWorkbook workbook = new XSSFWorkbook(is);
Sheet sheet = workbook.getSheetAt(0);
// 计算行数(不包括标题行)
int rowLength = sheet.getLastRowNum();
for (int rowNum = rowIndex; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row row = sheet.getRow(rowNum);
entity = (T) clazz.newInstance();
for (int j = cellIndex; j < row.getLastCellNum```java
(); j++) {
Cell cell = row.getCell(j);
for (Field field : fields) {
if (field.isAnnotationPresent(ExcelAttribute.class)) {
field.setAccessible(true);
ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class);
if (j == ea.sort()) {
field.set(entity, convertAttrType(field, cell));
}
}
}
}
list.add(entity);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
/**
* 将单元格数据转换为字段类型
*
* @param field 目标字段
* @param cell 单元格数据
* @return 转换后的字段值
* @throws Exception 可能抛出的异常
*/
private Object convertAttrType(Field field, Cell cell) throws Exception {
String fieldType = field.getType().getSimpleName();
if ("String".equals(fieldType)) {
return getValue(cell);
} else if ("Date".equals(fieldType)) {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(getValue(cell));
} else if ("int".equals(fieldType) || "Integer".equals(fieldType)) {
return Integer.parseInt(getValue(cell));
} else if ("double".equals(fieldType) || "Double".equals(fieldType)) {
return Double.parseDouble(getValue(cell));
} else {
return null;
}
}
/**
* 获取单元格的字符串值
*
* @param cell 单元格
* @return 单元格的字符串值
*/
public String getValue(Cell cell) {
if (cell == null) {
return "";
}
switch (cell.getCellType()) {
case STRING:
return cell.getRichStringCellValue().getString().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date dt = DateUtil.getJavaDate(cell.getNumericCellValue());
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(dt);
} else {
// 防止数值变成科学计数法
String strCell = "";
Double num = cell.getNumericCellValue();
BigDecimal bd = new BigDecimal(num.toString());
if (bd != null) {
strCell = bd.toPlainString();
}
// 去除浮点型自动加的 .0
if (strCell.endsWith(".0")) {
strCell = strCell.substring(0, strCell.indexOf("."));
}
return strCell;
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
default:
return "";
}
}
}
步骤 1: 创建 Excel 文件
- 创建一个 Excel 文件,确保其格式与模板文件的格式相匹配,包括列名、排序索引和格式。
步骤 2: 调用导入方法
java
// 示例代码
List<User> users = new ExcelImportUtil(User.class).readExcel(file.getInputStream(), 1, 1);
// TODO: 批量保存用户
- 创建
ExcelImportUtil
对象,传入数据对象的类。 - 获取 Excel 文件的输入流。
- 调用
readExcel
方法,传入输入流、数据起始行索引和数据起始列索引。
注意事项
- 确保 Excel 模板文件的格式与数据对象的属性和注解配置相匹配,以便正确地处理数据和样式。
- 完成保存数据的逻辑以完成导入操作。
Excel 数据对象示例 - EmployeeReportResult
以下是一个示例数据对象的定义,用于导出员工报表:
java
@Getter
@Setter
@NoArgsConstructor
@ToString
public class EmployeeReportResult {
@ExcelAttribute(sort = 0)
private String userId;
@ExcelAttribute(sort = 1)
private String username;
}
这个示例数据对象使用了 Lombok 注解,以简化属性的定义。属性上添加了 @ExcelAttribute
注解,用于定义与 Excel 表格相关的属性。
Excel 导入和导出工具示例代码
下面是用于导入和导出 Excel 数据的示例代码:
导出数据示例
java
// 导出员工报表数据
List<EmployeeReportResult> results = userCompanyPersonalMapper.findByReport(companyId, month + "%");
InputStream fis = new FileInputStream("path/to/your/template.xlsx");
new ExcelExportUtil(EmployeeReportResult.class, 2, 2)
.export(response, fis, results, "员工报表.xlsx");
导入数据示例
java
// 导入用户数据
List<User> users = new ExcelImportUtil(User.class).readExcel(file.getInputStream(), 1, 1);
// TODO: 批量保存用户
这些示例代码演示了如何使用 Excel 数据导入和导出工具来处理 Excel 数据。你可以根据这些示例代码来实现自己的数据导入和导出功能。