Excel 数据导入和导出工具使用手册

Excel 数据导入和导出工具使用手册

这篇文档将详细介绍如何使用 Excel 数据导入和导出工具来有效地处理 Excel 文件的导入和导出操作。这些工具包括三个主要类:ExcelExportUtilExcelImportUtilExcelAttribute

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 数据。你可以根据这些示例代码来实现自己的数据导入和导出功能。

相关推荐
蓝澈112124 分钟前
迪杰斯特拉算法之解决单源最短路径问题
java·数据结构
Kali_0731 分钟前
使用 Mathematical_Expression 从零开始实现数学题目的作答小游戏【可复制代码】
java·人工智能·免费
rzl0243 分钟前
java web5(黑马)
java·开发语言·前端
时序数据说1 小时前
为什么时序数据库IoTDB选择Java作为开发语言
java·大数据·开发语言·数据库·物联网·时序数据库·iotdb
君爱学习1 小时前
RocketMQ延迟消息是如何实现的?
后端
guojl1 小时前
深度解读jdk8 HashMap设计与源码
java
Falling421 小时前
使用 CNB 构建并部署maven项目
后端
guojl1 小时前
深度解读jdk8 ConcurrentHashMap设计与源码
java
程序员小假1 小时前
我们来讲一讲 ConcurrentHashMap
后端