Hutool excel导出

easyExcel 文档不是很友好,所以转用hutool 工具导出excel,百度到的很多都没多少营养,略作总结,望君不吝赐教。
该篇博客可以让你画出不是太复杂的excel 如:


image.png

ps: 该篇文章为代码画excel,不介绍模板填充方式,因为模板填充较为简单。

excel导出Hutool官方教程
excel导出Hutool api 地址
https://apidoc.gitee.com/loolly/hutool/cn/hutool/poi/excel/package-frame.html

版本依赖,项目中直接引入了hutool-all依赖包,其中包括了导出excel所需的所有依赖包

<dependency>
     <groupId>cn.hutool</groupId>
     <artifactId>hutool-all</artifactId>
     <version>5.5.0</version>
</dependency>

api 中已经详细写了方法的说明,再此不多加阐述

1.字体样式常用方法

    /**
     * 方法描述: 设置基础字体样式字体 这里保留最基础的样式使用
     *
     * @param workbook 工作簿
     * @param bold     是否粗体
     * @param fontName 字体名称
     * @param fontSize 字体大小
     * @return org.apache.poi.ss.usermodel.Font
     * @author wqf
     * @date 2021/5/19 15:58
     */
    public static Font setBaseFont(Workbook workbook, boolean bold, boolean italic, String fontName, int fontSize) {
        Font font = workbook.createFont();
        //设置字体名称 宋体 / 微软雅黑 /等
        font.setFontName(fontName);
        //设置是否斜体
        font.setItalic(italic);
        //设置字体高度
        //font.setFontHeight((short) fontHeight);
        //设置字体大小 以磅为单位
        font.setFontHeightInPoints((short) fontSize);
        //设置是否加粗
        font.setBold(bold);
        //默认字体颜色
        // font.setColor(Font.COLOR_NORMAL);
        //红色
        //font.setColor(Font.COLOR_RED);
        //设置下划线样式
        //font.setUnderline(Font.ANSI_CHARSET);
        //设定文字删除线
        //font.setStrikeout(true);
        return font;
    }
  1. 全局样式设置
 private static StyleSet GlobalStyleSet(ExcelWriter writer, Workbook workbook,Font font) {
        //全局样式设置
        StyleSet styleSet = writer.getStyleSet();
        CellStyle cellStyle = styleSet.getCellStyle();
        //设置全局文本居中
        styleSet.setAlign(HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
        //设置全局字体样式
        styleSet.setFont(font);
        //设置背景颜色 第二个参数表示是否将样式应用到头部
        styleSet.setBackgroundColor(IndexedColors.WHITE, true);
        //设置自动换行 当文本长于单元格宽度是否换行
        styleSet.setWrapText();
        // 设置全局边框样式
        styleSet.setBorder(BorderStyle.THIN, IndexedColors.BLACK);
        return styleSet;
    }
  1. 头部标题样式
//设置全局样式
StyleSet styleSet = GlobalStyleSet(writer, workbook);
//设置头部标题样式
CellStyle headCellStyle = styleSet.getHeadCellStyle();
//水平居中
headCellStyle.setAlignment(HorizontalAlignment.CENTER);
//垂直居中
headCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//设置字体样式
headCellStyle.setFont(setBaseFont(workbook, true, false, "宋体", 12));
writer.setStyleSet(styleSet);
  1. 数字保留小数
    例:保留两位小数
CellStyle cellStyleForNumber = styleSet.getCellStyleForNumber();
cellStyleForNumber.setDataFormat((short)2);

5.时间格式化
例如格式为:YYYY/MM/dd 格式

CellStyle cellStyleForDate = styleSet.getCellStyleForDate();
//14 代表的时间格式是 yyyy/MM/dd
cellStyleForDate.setDataFormat((short)14);

时间格式占时只看到这一种格式常见些,像yyyy-MM-dd 格式都没找到,就只有在写入数据写先处理下时间格式了。

  1. 行(Row)样式
//获取输出构造器 设置工作簿名称
ExcelWriter writer = ExcelUtil.getWriterWithSheet(sheetName);
Workbook workbook = writer.getWorkbook();
Sheet sheet = writer.getSheet();
Row row = sheet.getRow(rowIndex);
 if(sheet.getRow(rowIndex )==null){
      //rowIndex 表示的是第几行,例:创建第二行,rowIndex=1
      sheet.createRow(rowIndex );
  }
//创建样式
CellStyle cellStyle = workbook.createCellStyle();
cellStyle .setVerticalAlignment(VerticalAlignment.CENTER);
cellStyle .setAlignment(HorizontalAlignment.LEFT);
cellStyle .setFont(setBaseFont(workbook, true, false, "宋体", 12));
cellStyle .setBorderBottom(BorderStyle.THIN);
cellStyle .setBorderLeft(BorderStyle.THIN);
cellStyle .setBorderRight(BorderStyle.THIN);
cellStyle .setBorderTop(BorderStyle.THIN);
//应用样式到某一行(
row .setRowStyle(cellStyle );
//应用样式到某一行 (或者这样写) rowIndex  表示的是第几行
//writer.setRowStyle(rowIndex ,cellStyle );
  1. 单元格(Cell)样式
Row row = sheet.getRow(rowIndex);
if(sheet.getRow(rowIndex )==null){
      //rowIndex 表示的是第几行,例:创建第二行,rowIndex=1
      sheet.createRow(rowIndex );
  }
//创建本行的第几个单元格  cellIndex=0 表示第一个
if(row.get(cellIndex)==null){
    row .createCell(cellIndex);
}

//创建样式
CellStyle cellStyle = workbook.createCellStyle();
cellStyle .setVerticalAlignment(VerticalAlignment.CENTER);
cellStyle .setAlignment(HorizontalAlignment.LEFT);
cellStyle .setFont(setBaseFont(workbook, true, false, "宋体", 12));
cellStyle .setBorderBottom(BorderStyle.THIN);
cellStyle .setBorderLeft(BorderStyle.THIN);
cellStyle .setBorderRight(BorderStyle.THIN);
cellStyle .setBorderTop(BorderStyle.THIN);
//应用样式到某一行(
row .setRowStyle(cellStyle );
//应用样式到某一行 (或者这样写) rowIndex  表示的是第几行
//writer.setRowStyle(rowIndex ,cellStyle );
  1. 合并单元格
    //处理标题行 合并某行的单元格,并写入对象到单元格,如果写到单元格中的内容非null,行号自动+1,否则当前行号不变
//主要有两种方式
1. writer.merge(cellIndex, content, true);  
表示当前行 合并从第一个单元到cellIndex+1个单元,并填充内容content,第三个参数表示是否将头部标题样式应用到这里。
或者
2.writer.merge(startRowIndex,endRowIndex, startCellIndex, endCellIndex, content, false);
表示和并第startRowIndex+1行到endRowIndex+1行 ,并合并从第endCellIndex+1个单元到endCellIndex+1个单元格,并填充content内容,最后一个字段表示是否将头部标题样式应用到这里。
  1. 列表别名
//LinkedHashMap 中的数据是根据put先后顺序来的,HashMap数据时无序的。
//使用方法 writer.setHeaderAlias(headerAlias); 时如果使用HashMap 可能展示的数
//据顺序会错乱
Map<String, String> headerAlias = new LinkedHashMap<>();
headerAlias.put(字段名1, 列名1);
headerAlias.put(字段名2, 列名2);
headerAlias.put(字段名3, 列名3);
headerAlias.put(字段名4, 列名4);
headerAlias.put(字段名5, 列名5);
writer.setHeaderAlias(headerAlias);
//或者一项一项设置列的别名 列别名顺序会跟代码中addHeaderAlias顺序一致
writer.addHeaderAlias(字段名1,列名1);
writer.addHeaderAlias(字段名2,列名2);
  1. 列宽问题
    8.1 自动列宽 百度查到一个方法有用 ,但实际好像还是会有点问题,大家可以先试试
    博客链接 :https://blog.csdn.net/kongbai953/article/details/110382544
/**
     * 自适应宽度(中文支持)
     * @param sheet
     * @param size 因为for循环从0开始,size值为 列数-1
     */
    public static void setSizeColumn(Sheet sheet, int size) {
        for (int columnNum = 0; columnNum <= size; columnNum++) {
            int columnWidth = sheet.getColumnWidth(columnNum) / 256;
            for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row currentRow;
                //当前行未被使用过
                if (sheet.getRow(rowNum) == null) {
                    currentRow = sheet.createRow(rowNum);
                } else {
                    currentRow = sheet.getRow(rowNum);
                }

                if (currentRow.getCell(columnNum) != null) {
                    Cell currentCell = currentRow.getCell(columnNum);
                    if (currentCell.getCellType() == XSSFCell.CELL_TYPE_STRING) {
                        int length = currentCell.getStringCellValue().getBytes().length;
                        if (columnWidth < length) {
                            columnWidth = length;
                        }
                    }
                }
            }
            sheet.setColumnWidth(columnNum, columnWidth * 256);
        }
    }

8.2 手动列宽设置

//表示 第一列的列宽是15
 writer.setColumnWidth(0, 15);
  1. 另外常用方法

//跳过当前行 即当前行不写内容

writer.passCurrentRow();

//定位到最后一行,常用于在末尾追加数据

writer.setCurrentRowToEnd();

10 . 下载excel代码 在数据都填充完成后,调用该方法即可

 /**
     * 方法描述: 下载excel文件
     *
     * @param response 响应
     * @param fileName 文件名称
     * @param writer   writer
     * @return void
     * @author wqf
     * @date 2021/5/24 16:20
     */
    private static void downloadExcel(HttpServletResponse response, String fileName, ExcelWriter writer) {
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        // test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
        ServletOutputStream out = null;
        try {
            // 设置请求头属性
            response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName + ".xlsx").getBytes(), StandardCharsets.ISO_8859_1));
            out = response.getOutputStream();
            // 写出到文件
            writer.flush(out, true);
            // 关闭writer,释放内存
            writer.close();
            // 此处记得关闭输出Servlet流
            IoUtil.close(out);
        } catch (IOException e) {
            log.error(e.getMessage());
            e.printStackTrace();
        }
    }
  1. excel 添加下拉框
CellRangeAddressList addressList = new CellRangeAddressList(2, 2, 5, 5);
DataValidationHelper helper = sheet.getDataValidationHelper();
 // 设置下拉框数据
String[] str = new String[]{"男", "女","阴阳人"};
DataValidationConstraint constraint = helper.createExplicitListConstraint(str);
DataValidation dataValidation = helper.createValidation(constraint, addressList);
writer.addValidationData(dataValidation);

12.背景色填充

//示例:将单元格背景填充为黄色
short index = IndexedColors.YELLOW.index;
cellStyle.setFillForegroundColor(index);
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

总结 : 整合了一个工具类,仅作参考

/**
 * @Author: wqf
 * @Date: 2021/05/28
 * @Description: hutool 工具导出excel(非填充模板,手工画)
 */
@Slf4j
public class HutoolExcelUtil {
    /**
     * YYYY/MM/dd 时间格式
     */
    private static final short LOCAL_DATE_FORMAT_SLASH = 14;

    /**
     * 方法描述: 创建excel
     *
     * @param isXlsx excel文件类型 true-xlsx/false-xls
     * @return cn.hutool.poi.excel.ExcelWriter
     * @author wqf
     * @date 2021/6/1 9:47
     */
    public static ExcelWriter createExcel(boolean isXlsx) {
        return ExcelUtil.getWriter(isXlsx);
    }

    /**
     * 方法描述: 全局基础样式设置
     * 默认 全局水平居中+垂直居中
     * 默认 自动换行
     * 默认单元格边框颜色为黑色,细线条
     * 默认背景颜色为白色
     *
     * @param writer writer
     * @param font   字体样式
     * @return cn.hutool.poi.excel.StyleSet
     * @author wqf
     * @date 2021/5/28 10:43
     */
    public static StyleSet setBaseGlobalStyle(ExcelWriter writer, Font font) {
        //全局样式设置
        StyleSet styleSet = writer.getStyleSet();
        //设置全局文本居中
        styleSet.setAlign(HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
        //设置全局字体样式
        styleSet.setFont(font, true);
        //设置背景颜色 第二个参数表示是否将样式应用到头部
        styleSet.setBackgroundColor(IndexedColors.WHITE, true);
        //设置自动换行 当文本长于单元格宽度是否换行
        //styleSet.setWrapText();
        // 设置全局边框样式
        styleSet.setBorder(BorderStyle.THIN, IndexedColors.BLACK);
        return styleSet;
    }

    /**
     * 方法描述: 设置标题的基础样式
     *
     * @param styleSet            StyleSet
     * @param font                字体样式
     * @param horizontalAlignment 水平排列方式
     * @param verticalAlignment   垂直排列方式
     * @return org.apache.poi.ss.usermodel.CellStyle
     * @author wqf
     * @date 2021/5/28 10:16
     */
    public static CellStyle createHeadCellStyle(StyleSet styleSet, Font font,
                                                HorizontalAlignment horizontalAlignment,
                                                VerticalAlignment verticalAlignment) {
        CellStyle headCellStyle = styleSet.getHeadCellStyle();
        headCellStyle.setAlignment(horizontalAlignment);
        headCellStyle.setVerticalAlignment(verticalAlignment);
        headCellStyle.setFont(font);
        return headCellStyle;
    }

    /**
     * 方法描述: 设置基础字体样式字体 这里保留最基础的样式使用
     *
     * @param bold     是否粗体
     * @param fontName 字体名称
     * @param fontSize 字体大小
     * @return org.apache.poi.ss.usermodel.Font
     * @author wqf
     * @date 2021/5/19 15:58
     */
    public static Font createFont(ExcelWriter writer, boolean bold, boolean italic, String fontName, int fontSize) {
        Font font = writer.getWorkbook().createFont();
        //设置字体名称 宋体 / 微软雅黑 /等
        font.setFontName(fontName);
        //设置是否斜体
        font.setItalic(italic);
        //设置字体大小 以磅为单位
        font.setFontHeightInPoints((short) fontSize);
        //设置是否加粗
        font.setBold(bold);
        return font;
    }

    /**
     * 方法描述: 设置行或单元格基本样式
     *
     * @param writer              writer
     * @param font                字体样式
     * @param verticalAlignment   垂直居中
     * @param horizontalAlignment 水平居中
     * @return void
     * @author wqf
     * @date 2021/5/28 10:28
     */
    public static CellStyle createCellStyle(ExcelWriter writer, Font font, boolean wrapText,
                                            VerticalAlignment verticalAlignment,
                                            HorizontalAlignment horizontalAlignment) {
        CellStyle cellStyle = writer.getWorkbook().createCellStyle();
        cellStyle.setVerticalAlignment(verticalAlignment);
        cellStyle.setAlignment(horizontalAlignment);
        cellStyle.setWrapText(wrapText);
        cellStyle.setFont(font);
        return cellStyle;
    }

    /**
     * 方法描述: 设置边框样式
     *
     * @param cellStyle 样式对象
     * @param bottom    下边框
     * @param left      左边框
     * @param right     右边框
     * @param top       上边框
     * @return void
     * @author wqf
     * @date 2021/5/28 10:23
     */
    public static void setBorderStyle(CellStyle cellStyle, BorderStyle bottom, BorderStyle left, BorderStyle right,
                                      BorderStyle top) {
        cellStyle.setBorderBottom(bottom);
        cellStyle.setBorderLeft(left);
        cellStyle.setBorderRight(right);
        cellStyle.setBorderTop(top);
    }

    /**
     * 方法描述: 获取对象需要导出的列和别名 这里按字段顺序来(可以在自定义注解上添加属性标识字段顺序,重写方法)
     * 在需要导出的字段上贴上注解 这里是@ExcelProperty,也可以用自定义注解
     * 注解需要有value 标识别名 order 标识字段顺序
     *
     * @param clazz 对象类型
     * @return int 导出的字段个数
     * @author wqf
     * @date 2021/4/29 16:56
     */
    public static int setHeaderAlias(ExcelWriter writer, Class<?> clazz) {
        //需要导出的字段数
        Field[] fields = clazz.getDeclaredFields();
        TreeMap<Integer, Map<String, String>> headerAliasMap = new TreeMap<>();
        Arrays.stream(fields).forEach(f -> {
            if (f.isAnnotationPresent(ExcelProperty.class)) {
                ExcelProperty annotation = f.getAnnotation(ExcelProperty.class);
                //别名
                String[] value = annotation.value();
                int order = annotation.order();
                Map<String, String> alisMap = new HashMap<>();
                String fieldName = f.getName();
                //字段名
                String headerAlias = value[0];
                alisMap.put(fieldName, headerAlias);
                headerAliasMap.put(order, alisMap);
            }
        });
        LinkedHashMap<String, String> linkedMap = new LinkedHashMap<>();
        for (Map.Entry<Integer, Map<String, String>> entry : headerAliasMap.entrySet()) {
            linkedMap.putAll(entry.getValue());
        }
        writer.setHeaderAlias(linkedMap);
        return linkedMap.size();
    }

    /**
     * 方法描述: 自适应宽度(中文支持)
     * 
     * @param sheet 页
     * @param size  因为for循环从0开始,size值为 列数-1
     * @return void
     * @author wqf
     * @date 2021/5/28 14:06
     */
    public static void setSizeColumn(Sheet sheet, int size) {
        for (int columnNum = 0; columnNum <= size; columnNum++) {
            int columnWidth = sheet.getColumnWidth(columnNum) / 256;
            for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row currentRow;
                //当前行未被使用过
                if (sheet.getRow(rowNum) == null) {
                    currentRow = sheet.createRow(rowNum);
                } else {
                    currentRow = sheet.getRow(rowNum);
                }

                if (currentRow.getCell(columnNum) != null) {
                    Cell currentCell = currentRow.getCell(columnNum);
                    if (currentCell.getCellType() == CellType.STRING) {
                        int length = currentCell.getStringCellValue().getBytes().length;
                        if (columnWidth < length) {
                            columnWidth = length;
                        }
                    }
                }
            }
            sheet.setColumnWidth(columnNum, columnWidth * 256);
        }
    }

    /**
     * 方法描述: excel 导出下载
     *
     * @param response 响应
     * @param fileName 文件名
     * @param writer   writer
     * @return void
     * @author wqf
     * @date 2021/6/1 11:11
     */
    public static void downloadExcel(HttpServletResponse response, String fileName, ExcelWriter writer) {
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        // test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
        ServletOutputStream out = null;
        try {
            // 设置请求头属性
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + new String((fileName + ".xlsx").getBytes(), StandardCharsets.ISO_8859_1));
            out = response.getOutputStream();
            // 写出到文件
            writer.flush(out, true);
            // 关闭writer,释放内存
            writer.close();
            // 此处记得关闭输出Servlet流
            IoUtil.close(out);
        } catch (IOException e) {
            log.info("文件下载失败==" + e);
        }
    }

    /**
     * 方法描述: 最简单的导出,直接将数据放到excel
     * 导入的数据最好是List<map>格式数据,主要原因是List<Object> ,
     * 对象有字段是LocalDate或者是LocalDateTime类型的数据,
     * 时间格式化可能达不到想要的效果
     *
     * @param data       对象集合 两种
     * @param globalFont 全局字体样式
     * @param clazz      导出的对应class类型
     * @return void
     * @author wqf
     * @date 2021/5/28 11:22
     */
    public static ExcelWriter exportBaseExcel(ExcelWriter writer, List<?> data, Font globalFont, Class clazz) {
        Sheet sheet = writer.getSheet();
        writer.setStyleSet(setBaseGlobalStyle(writer, globalFont));
        int columnSize = setHeaderAlias(writer, clazz);
        writer.setOnlyAlias(true);
        writer.write(data);
        setSizeColumn(sheet, columnSize);
        return writer;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,783评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,396评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,834评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,036评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,035评论 5 362
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,242评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,727评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,376评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,415评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,463评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,140评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,734评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,809评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,028评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,521评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,119评论 2 341

推荐阅读更多精彩内容