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

相关推荐
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2342 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨2 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
Chrikk3 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*3 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man3 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟3 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity4 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天4 小时前
java的threadlocal为何内存泄漏
java