基于Java编写一个实用的ExcelUtil工具类

 更新时间:2024年04月09日 08:22:04   作者:小城边  
在项目中经常遇到excel表格导入导出功能,每次都要重复写有关excel 的逻辑,所以本文直接使用Java编写一个实用的ExcelUtil工具类,希望对大家有所帮助

场景:在项目中经常遇到excel表格导入导出功能,每次都要重复写有关excel 的逻辑,在网上找了一圈没有找到自己想要的,于是我就自己创建一个工具类,实现了简单的导入和导出功能。

一、首先引入 Maven 依赖

<!-- java poi -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>
<!--支持xlsx读取-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>3.17</version>
</dependency>

二、创建 ExcelUtil 工具类

创建了工具类,我们要实现的有导入导出两个主要功能

实现导入功能

导入功能实现要考虑几点:

  • Excel表格中的每一列如何和Java实体类中的属性关联。
  • 表头占几行是否需要去掉。
  • 多个 sheet 页是否都要导入。

我的代码如下:

/**
     *
     * @param file 导入的excel 文件
     * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名
     * @param excelTitleToPojoMap 列名对应java 中实体类属性名
     * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页
     * @param startRow 从第几行开始导入数据
     * @param clazz list 列表中的元素类型
     */
    public static <T> List<T> importExcelByFile(MultipartFile file,
                                              Map<Integer, String> excelRowToTitleMap,
                                              Map<String, String> excelTitleToPojoMap,
                                              List<String> sheetList, Integer startRow, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        try (InputStream fis = file.getInputStream()){
            list = importExcel(fis, excelRowToTitleMap, excelTitleToPojoMap, sheetList, startRow, clazz);
        } catch (Exception e) {
            log.info("import excel exception:{}", e.getMessage());
        }
        return list;
    }

    /**
     *
     * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名
     * @param excelTitleToPojoMap 列名对应java 中实体类属性名
     * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页
     * @param startRow 从第几行开始导入数据
     * @param clazz list 列表中的元素类型
     */
    public static <T> List<T> importExcel(InputStream inputStream,
                                        Map<Integer, String> excelRowToTitleMap,
                                        Map<String, String> excelTitleToPojoMap,
                                        List<String> sheetList, Integer startRow, Class<T> clazz) {
        Set<Integer> rows = excelRowToTitleMap.keySet();
        List<T> list = new ArrayList<>();
        try (Workbook workbook = WorkbookFactory.create(inputStream)) {
            for (Sheet sheet : workbook) {
                String sheetName = sheet.getSheetName();
                // 如果指定了要导入的 sheet 页,就需要过滤其他的
                if (sheetList != null && !sheetList.contains(sheetName)) {
                    continue;
                }
                for (Row row : sheet) {
                    // 去掉表头
                    if (row.getRowNum() + 1 < startRow) {
                        continue;
                    }
                    // 反射获取对象
                    T obj = clazz.newInstance();
                    for (int i = 0; i < row.getLastCellNum(); i++) {
                        Cell cell = row.getCell(i);
                        String cellValue = cell != null ? cell.toString() : null;
                        if (rows.contains(i + 1)) {
                            String rowTitleName = excelRowToTitleMap.get(i + 1);
                            String propertyName = excelTitleToPojoMap.get(rowTitleName);
                            if (propertyName != null) {
                                // 反射获取字段的set方法
                                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyName, clazz);
                                Method writeMethod = propertyDescriptor.getWriteMethod();
                                if (writeMethod != null) {
                                    Class<?> propertyType = propertyDescriptor.getPropertyType();
                                    Object value = convertValue(cellValue, propertyType);
                                    writeMethod.invoke(obj, value);
                                }
                            }
                        }
                    }
                    list.add(obj);
                }
            }
        } catch (Exception e) {
            log.info("import excel exception:{}", e.getMessage());
        }
        return list;
    }

上面代码中excelRowToTitleMap和excelTitleToPojoMap解决了Excel表格中的每一列如何和Java实体类中的属性关联关系。sheetList 列出了需要导入的 sheet 页,startRow 则是要从第几行读取数据。这样上面的几个问题就都解决了。

实现导出功能

项目中的常见的就是 list 导出到 excel。

同样的要考虑几个问题:

  • excel 表头可以自定义中文名。
  • 可以指定导出哪些字段。

我的代码如下:

/**
     * 将 list 导入 excel
     * @param list list 数据列表
     * @param obj list 的实体对象
     * @param header 表头数组  (为 null 时,实体类中属性名作为表头)
     * @param rows 导出的列  (为 null 导出所有列)
     * @param fileName 文件名称
     * @param response response
     */
    public static void exportListToExcel(List<?> list, Class obj, String[] header, String[] rows, String fileName, HttpServletResponse response) {
        try {
            //1.创建工作簿
            Workbook book = new XSSFWorkbook();

            //2.创建 sheet 页
            Sheet sheet = book.createSheet();

            //获取 get 方法
            List<Method> methods = new LinkedList<>();
            if (rows == null || rows.length == 0) {
                //获取实体类中所有列 get 方法
                BeanInfo beanInfo = Introspector.getBeanInfo(obj);
                MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
                for (int i = 0; i < methodDescriptors.length; i++) {
                    String method = methodDescriptors[i].getName();
                    if (Pattern.matches("[g][e][t]\\w*",method) && !"getClass".equals(method)) {
                        methods.add(methodDescriptors[i].getMethod());
                    }
                }
            } else {
                //获取指定列的 get 方法
                for (int i = 0; i < rows.length; i++) {
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(rows[i], obj);
                    methods.add(propertyDescriptor.getReadMethod());
                }
            }
            if (header == null || header.length == 0) {
                header = new String[methods.size()];
                for (int i = 0; i < header.length; i++) {
                    String methodName = methods.get(i).getName();
                    header[i] = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
                }
            }


            //3.创建表头
            Row headerRow = sheet.createRow(0);
            CellStyle headerStyle = getHeaderStyle(book);
            setHeaderRow(header, headerRow, headerStyle);

            //4.填充数据
            int sheetFlag = 0;
            if (!CollectionUtils.isEmpty(list)) {
                for (int i = 0; i < list.size(); i++) {
                    //超过最大行数,自动创建下一页
                    if ((i+1)%1048575 == 0) {
                        sheet = book.createSheet("sheet" + sheetFlag);
                        headerRow = sheet.createRow(0);
                        setHeaderRow(header, headerRow, headerStyle);
                        sheetFlag ++;
                    }
                    //getLastRowNum 获取行标(表头行标为 0)
                    Row row = sheet.createRow(sheet.getLastRowNum() + 1);
                    Object o = list.get(i);
                    for (int j = 0; j < methods.size(); j++) {
                        Object value = methods.get(j).invoke(o);
                        row.createCell(j).setCellValue(value != null ? value.toString() : "-");
                    }
                }
            }

            //5.将 excel 表格导出
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename="+new String(fileName.getBytes(StandardCharsets.UTF_8),"ISO8859-1") + ".xlsx");
            ServletOutputStream outputStream = response.getOutputStream();
            book.write(outputStream);
            outputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

我通过header和rows两个参数可以将excel 列表和实体类中的属性关联起来(两个数组中的元素位置要一一对应),然后通过反射获取到list列表元素写入到excel 中。

至此基础的导入和导出功能就ok了

完整的代码

package com.base.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Pattern;

@Slf4j
public class ExcelUtil {

    /**
     *
     * @param file 导入的excel 文件
     * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名
     * @param excelTitleToPojoMap 列名对应java 中实体类属性名
     * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页
     * @param startRow 从第几行开始导入数据
     * @param clazz list 列表中的元素类型
     */
    public static <T> List<T> importExcelByFile(MultipartFile file,
                                              Map<Integer, String> excelRowToTitleMap,
                                              Map<String, String> excelTitleToPojoMap,
                                              List<String> sheetList, Integer startRow, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        try (InputStream fis = file.getInputStream()){
            list = importExcel(fis, excelRowToTitleMap, excelTitleToPojoMap, sheetList, startRow, clazz);
        } catch (Exception e) {
            log.info("import excel exception:{}", e.getMessage());
        }
        return list;
    }

    /**
     *
     * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名
     * @param excelTitleToPojoMap 列名对应java 中实体类属性名
     * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页
     * @param startRow 从第几行开始导入数据
     * @param clazz list 列表中的元素类型
     */
    public static <T> List<T> importExcel(InputStream inputStream,
                                        Map<Integer, String> excelRowToTitleMap,
                                        Map<String, String> excelTitleToPojoMap,
                                        List<String> sheetList, Integer startRow, Class<T> clazz) {
        Set<Integer> rows = excelRowToTitleMap.keySet();
        List<T> list = new ArrayList<>();
        try (Workbook workbook = WorkbookFactory.create(inputStream)) {
            for (Sheet sheet : workbook) {
                String sheetName = sheet.getSheetName();
                // 如果指定了要导入的 sheet 页,就需要过滤其他的
                if (sheetList != null && !sheetList.contains(sheetName)) {
                    continue;
                }
                for (Row row : sheet) {
                    // 去掉表头
                    if (row.getRowNum() + 1 < startRow) {
                        continue;
                    }
                    // 反射获取对象
                    T obj = clazz.newInstance();
                    for (int i = 0; i < row.getLastCellNum(); i++) {
                        Cell cell = row.getCell(i);
                        String cellValue = cell != null ? cell.toString() : null;
                        if (rows.contains(i + 1)) {
                            String rowTitleName = excelRowToTitleMap.get(i + 1);
                            String propertyName = excelTitleToPojoMap.get(rowTitleName);
                            if (propertyName != null) {
                                // 反射获取字段的set方法
                                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyName, clazz);
                                Method writeMethod = propertyDescriptor.getWriteMethod();
                                if (writeMethod != null) {
                                    Class<?> propertyType = propertyDescriptor.getPropertyType();
                                    Object value = convertValue(cellValue, propertyType);
                                    writeMethod.invoke(obj, value);
                                }
                            }
                        }
                    }
                    list.add(obj);
                }
            }
        } catch (Exception e) {
            log.info("import excel exception:{}", e.getMessage());
        }
        return list;
    }


    /**
     * 将 list 导入 excel
     * @param list list 数据列表
     * @param obj list 的实体对象
     * @param header 表头数组  (为 null 时,实体类中属性名作为表头)
     * @param rows 导出的列  (为 null 导出所有列)
     * @param fileName 文件名称
     * @param response response
     */
    public static void exportListToExcel(List<?> list, Class obj, String[] header, String[] rows, String fileName, HttpServletResponse response) {
        try {
            //1.创建工作簿
            Workbook book = new XSSFWorkbook();

            //2.创建 sheet 页
            Sheet sheet = book.createSheet();

            //获取 get 方法
            List<Method> methods = new LinkedList<>();
            if (rows == null || rows.length == 0) {
                //获取实体类中所有列 get 方法
                BeanInfo beanInfo = Introspector.getBeanInfo(obj);
                MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
                for (int i = 0; i < methodDescriptors.length; i++) {
                    String method = methodDescriptors[i].getName();
                    if (Pattern.matches("[g][e][t]\\w*",method) && !"getClass".equals(method)) {
                        methods.add(methodDescriptors[i].getMethod());
                    }
                }
            } else {
                //获取指定列的 get 方法
                for (int i = 0; i < rows.length; i++) {
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(rows[i], obj);
                    methods.add(propertyDescriptor.getReadMethod());
                }
            }
            if (header == null || header.length == 0) {
                header = new String[methods.size()];
                for (int i = 0; i < header.length; i++) {
                    String methodName = methods.get(i).getName();
                    header[i] = methodName.substring(3,4).toLowerCase() + methodName.substring(4);
                }
            }


            //3.创建表头
            Row headerRow = sheet.createRow(0);
            CellStyle headerStyle = getHeaderStyle(book);
            setHeaderRow(header, headerRow, headerStyle);

            //4.填充数据
            int sheetFlag = 0;
            if (!CollectionUtils.isEmpty(list)) {
                for (int i = 0; i < list.size(); i++) {
                    //超过最大行数,自动创建下一页
                    if ((i+1)%1048575 == 0) {
                        sheet = book.createSheet("sheet" + sheetFlag);
                        headerRow = sheet.createRow(0);
                        setHeaderRow(header, headerRow, headerStyle);
                        sheetFlag ++;
                    }
                    //getLastRowNum 获取行标(表头行标为 0)
                    Row row = sheet.createRow(sheet.getLastRowNum() + 1);
                    Object o = list.get(i);
                    for (int j = 0; j < methods.size(); j++) {
                        Object value = methods.get(j).invoke(o);
                        row.createCell(j).setCellValue(value != null ? value.toString() : "-");
                    }
                }
            }

            //5.将 excel 表格导出
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename="+new String(fileName.getBytes(StandardCharsets.UTF_8),"ISO8859-1") + ".xlsx");
            ServletOutputStream outputStream = response.getOutputStream();
            book.write(outputStream);
            outputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Object convertValue(String cellValue, Class<?> targetType) {
        if (cellValue == null || targetType == null) {
            return null;
        }
        if (String.class.equals(targetType)) {
            return cellValue;
        } else if (Integer.class.equals(targetType) || int.class.equals(targetType)) {
            return Integer.valueOf(cellValue);
        } else if (Double.class.equals(targetType) || double.class.equals(targetType)) {
            return Double.valueOf(cellValue);
        } else if (Boolean.class.equals(targetType) || boolean.class.equals(targetType)) {
            return Boolean.valueOf(cellValue);
        }
        // 可以根据需要添加其他类型的转换
        return cellValue;
    }


    /**
     * 设置表头
     * @param header
     * @param headerRow
     * @param headerStyle
     */
    private static void setHeaderRow(String[] header, Row headerRow, CellStyle headerStyle) {
        for (int i = 0; i < header.length; i++) {
            Cell headerCell = headerRow.createCell(i);
            headerCell.setCellValue(header[i]);
            headerCell.setCellStyle(headerStyle);
        }
    }

    /**
     * 表头样式
     * @param book
     */
    private static CellStyle getHeaderStyle(Workbook book) {
        CellStyle headerStyle = book.createCellStyle();
        Font headerFont = book.createFont();
        headerFont.setBold(true); //设置粗体
        headerFont.setFontName("表头加粗字体");
        headerStyle.setFont(headerFont);
        return headerStyle;
    }
}

以上就是基于Java编写一个实用的ExcelUtil工具类的详细内容,更多关于Java ExcelUtil工具类的资料请关注脚本之家其它相关文章!

相关文章

最新评论