基于springboot的动态表头excel导出

一,背景

        在项目里面基本上,只要遇到报表或者数据表格都需要进行导出,每个页面写一个导出方法又太麻烦。经过多个项目的练手,我觉得需要把导出方法进行封装或者说模块化,这样无论是前端调用还是后端调用都只需要提供表头和数据,就可以进行excel的导出不必一个页面写一个导出。

二,模块化逻辑

        只要是excel的导出必定有表头,数据内容,那么调用导出模块时只需要两种数组,①表头部分,②数据部分  表头和数据都是动态的 这样就实现了动态表头功能 

请求示例
     {
              //表头
          "table_name_head": [
        {
            "label": "经销商编码",
            "key": "dealer_code"
        },
        {
            "label": "经销商名称",
            "key": "dealer_name"
        },
        {
            "label": "创建人",
            "key": "create"
        },
        {
            "label": "地区",
            "key": "region"
        }
    ],
   //excel数据
        "data": [
   {
     "dealer_code": "5645343",
     "dealer_name": "xxxxxx",
     "create": "JNJX",
     "region": "North"
     }
     ]
     }
    

三,通用导出模块代码

模块使用了三个参数 

ExcelUtil.exportExcel("excelTemplate",map,response);
excelTemplate //模板名称
map // 里面存在两个元素 表头数组 ,数据数组
response //HttpServletResponse
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * @PACKAGE_NAME: com.spike.util.controller
 * @USER: spike
 * @DATE: 2023/4/27 15:46
 * @PROJECT_NAME: Springcolud_Spike
 */
@Slf4j
public class ExcelUtil {

    /**
     * 判断excel文件类型正则表达式
     */
    private static final String IS_EXCEL = "^.+\\.(?i)((xls)|(xlsx))$";

    /**
     * 判断是否为 xls后缀版本的excel
     */
    private static final String IS_XLS_EXCEL = "^.+\\.(?i)(xls)$";

    /**
     * 存放excel模板的目录
     */
    private static final String CLASS_PATH = "templates/excel/";
    
    /**
     * 动态表头
     */
    public static final String TABLE_NAME_HEAD = "table_name_head";

    public static final String LABEL = "label";

    public static final String KEY = "key";

    public static void exportExcel(String execlTempleName, Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.ExcelTemplate excelTemplate = ExcelUtil.ExcelTemplate.init(execlTempleName + ".xlsx");
        Workbook workbook = excelTemplate.getWorkbook();
        Sheet sheetAt = workbook.getSheetAt(0);
        //获取动态表头
        if (map.get(TABLE_NAME_HEAD) == null || !(map.get(TABLE_NAME_HEAD) instanceof List)) {
            log.info("动态标题格式异常,导出失败!");
            return;
        }
        //动态表头
        List<Map<String, Object>> tableHead = (List<Map<String, Object>>) map.get(TABLE_NAME_HEAD);
        //设置表头
        Row tempRow = sheetAt.createRow(0);
        int col = 0;
        for (Map<String, Object> tableHeadMap: tableHead) {
            String tableName = tableHeadMap.get(LABEL).toString();
            tempRow.createCell(col++).setCellValue(tableName);
        }
        //填充表内容
        int rowValue = 1;
        //创建数据行
        if (map.get("data") == null || !(map.get("data") instanceof List)) {
            log.info("数据格式异常,导出失败!");
            return;
        }
        List<Map> dataList = (List<Map>) map.get("data");
        Row row = sheetAt.createRow(rowValue);
        for (Map dataMap : dataList) {
            row = sheetAt.createRow(rowValue);
            col = 0;
            for (Map<String, Object> tableHeadMap : tableHead) {
                String fileName =  tableHeadMap.get(KEY).toString();
                String value = dataMap.get(fileName) != null ? dataMap.get(fileName).toString() : "";
                if(!value.isEmpty()&& Pattern.matches("[-]?[0-9]*[.]?[0-9]*",value)){
                    row.createCell(col).setCellType(CellType.NUMERIC);
                    double dobValue= Double.valueOf(value);
                    row.createCell(col++).setCellValue(dobValue);
                }else {
                    row.createCell(col).setCellType(CellType.STRING);
                    row.createCell(col++).setCellValue(value);
                }
            }
            rowValue = rowValue + 1;
        }
        String fileName = "DFlow" + System.currentTimeMillis();
        excelTemplate.export(response, fileName);
        return;
    }

    /**
     * 获取response输出流
     *
     * @param fileName 文件名
     * @param response 响应体
     * @return OutputStream
     */
    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
        try {
            fileName = URLEncoder.encode(fileName + ".xlsx", "UTF-8");
            response.setContentType("octets/stream");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ";" + "filename*=utf-8''" + fileName);
            return response.getOutputStream();
        } catch (IOException e) {
            log.error("设置excel输出流异常!文件名:{}", fileName, e);
            throw e;
        }
    }

    /**
     * 构建 Workbook 对象
     *
     * @param templateName 模板文件名
     * @return Workbook
     */
    public static Workbook buildWorkbook(String templateName) throws Exception {
        Workbook workbook;
        try {
            if (templateName.matches(IS_EXCEL)) {
                // 得到文件输入流对象
                InputStream inputStream = ExcelUtil.class.getClassLoader().getResourceAsStream(CLASS_PATH + templateName);
                if (inputStream == null) {
                    throw new Exception("找不到excel模板文件");
                }

                boolean isXlsExcel = templateName.matches(IS_XLS_EXCEL);
                // 创建工作簿,并传递要读取的文件
                workbook = isXlsExcel ? new HSSFWorkbook(inputStream) : new XSSFWorkbook(inputStream);
                inputStream.close();
            } else {
                throw new Exception("文件格式不是xls或xlsx");
            }
            return workbook;
        } catch (Exception e) {
            log.info("获取Workbook对象异常!msg:{}", e.getMessage(), e);
            throw e;
        }

    }

    /**
     * excel 模板类
     */
    public static class ExcelTemplate {

        private Workbook workbook;

        /**
         * 通过文件名称读取模板文件并初始化ExcelTemplate对象
         *
         * @param fileName 模板文件名
         * @return ExcelTemplate
         */
        public static ExcelTemplate init(String fileName) throws Exception {
            ExcelTemplate excelTemplate = new ExcelTemplate();
            Workbook workbook = ExcelUtil.buildWorkbook(fileName);
            excelTemplate.setWorkbook(workbook);
            return excelTemplate;
        }

        /**
         * 导出excel
         *
         * @param response     响应体
         * @param newExcelName 自定义导出excel的文件名
         */
        public void export(HttpServletResponse response, String newExcelName) throws Exception {
            OutputStream out = null;
            try {
                out = getOutputStream(newExcelName, response);
                ByteArrayOutputStream ops = new ByteArrayOutputStream();
                workbook.write(ops);
                out.write(ops.toByteArray());
            } finally {

                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

        public void setWorkbook(Workbook workbook) {
            this.workbook = workbook;
        }

        public Workbook getWorkbook() {
            return workbook;
        }
    }
}

maven依赖 

 <!--execl依赖-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>

导出模块使用的是模板填充 所以还需要在resources 下面添加空白的excel模板

 新建一个excel文件作为模板即可

        基本的导出模块已经完成 只需要把导出模块放在工具类中,在service中调用就可以了(也可以把接口放开直接让前端传动态表头和需要导出的数据),非常方便摸鱼

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值