Spring Boot Excel导出一

736 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

简介

在业务系统开发中经常会遇到一些报表功能,其中包含excel导出,在java领域中成熟的excel导出实现方案有poi、jxl、esayExcel等,今天给大家介绍jxls通过模板方式导出excel。

Jxls是一个小型Java库,可以轻松生成Excel报告。Jxls使用Excel模板中的特殊标记来定义输出格式和数据布局。

特性

  • XML和二进制Excel格式输出(取决于底层Java到Excel的实现)
  • 按行和列输出的Java集合
  • 条件输出
  • 报表定义标记中的表达式语言
  • 多页输出
  • 原生Excel公式
  • 参数化公式
  • 分组支持
  • 合并单元格支持
  • 用于调整excel生成的区域侦听器
  • 命令定义的Excel注释标记
  • 命令定义的XML标记
  • 自定义命令定义
  • 流媒体可实现快速输出和更少的内存消耗
  • 为所选图纸流式处理(选择图纸流式处理)

官网地址: jxls.sourceforge.net/

实现步骤

1.引入相关jar包

	<dependency>
			<groupId>org.jxls</groupId>
			<artifactId>jxls</artifactId>
			<version>2.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.jxls</groupId>
			<artifactId>jxls-poi</artifactId>
			<version>1.0.11</version>
		</dependency>

2.封装核心导出工具类


    private String template;
    private boolean hideTemplateSheet = false;
    private boolean deleteTemplateSheet = true;
    private boolean processFormulas = true;
    private boolean useFastFormulaProcessor = true;
    private int rowAccessWindowSize = 100;
    private boolean isSXSSFWorkbook = false;
    private AreaBuilder areaBuilder = new XlsCommentAreaBuilder();
    protected Writer writer;
    protected OutputStream os;
    
    //省略get、set方法

public void export(Map<String, Object> map, String shirtName)
    {
        logger.info("export excel begin...");
        InputStream is = null;
        Workbook book = null;
        Transformer transformer = null;
        Context context = null;

        try
        {
            is = new FileInputStream(new File(template));
            if (isSXSSFWorkbook)
            {
                book = WorkbookFactory.create(is);
                transformer = PoiTransformer.createSxssfTransformer(book,
                        rowAccessWindowSize, true);
                context = new PoiContext(map);
            }
            else
            {
                transformer = TransformerFactory.createTransformer(is, os);
                context = new Context(map);
            }

            JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator) transformer
                    .getTransformationConfig().getExpressionEvaluator();

            // 设置静默模式,不报警告
            evaluator.getJexlEngine().setSilent(true);
            areaBuilder.setTransformer(transformer);
            List<Area> xlsAreaList = areaBuilder.build();
            if (xlsAreaList.isEmpty())
            {
                logger.error("No XlsArea were detected for this processing");
                return;
            }
            Area xlsArea = xlsAreaList.get(0);
            CellRef targetCellRef = new CellRef(shirtName);
            xlsArea.applyAt(targetCellRef, context);
            if (processFormulas)
            {
                setFormulaProcessor(xlsArea);
                xlsArea.processFormulas();
            }
            String sourceSheetName = xlsArea.getStartCellRef().getSheetName();
            if (!sourceSheetName.equalsIgnoreCase(targetCellRef.getSheetName()))
            {
                if (hideTemplateSheet)
                {
                    transformer.setHidden(sourceSheetName, true);
                }

                if (deleteTemplateSheet)
                {
                    transformer.deleteSheet(sourceSheetName);
                }
            }
            if (transformer instanceof PoiTransformer)
            {
                ((PoiTransformer) transformer).getWorkbook().write(os);
            }
            else
            {
                transformer.write();
            }
        }
        catch (Exception e)
        {
            logger.error("xls export error", e);
        }
        finally
        {
            if(is!=null)
            {
                try
                {
                    is.close();
                }
                catch (IOException e)
                {
                    logger.error("close io error",e);
                }
            }
        }
        logger.info("export excel finished");
    }
    
    /**
     * 导出excel
     * 
     * @param template
     *            模板文件路径
     * @param fileName
     *            导出文件名称
     * @param data
     *            数据集合
     * @param response
     *            reponse响应
     */
    protected void exportXls(String template, String fileName,
            Map<String, Object> data, HttpServletResponse response)
    {
        logger.info("excel template:" + template);
        String ext =getFileExt(template);
        XlsExporter exporter = null;
        try
        {
            fileName = encodeFileName(fileName);
            if (ext != null)
            {
                fileName += "." + ext;
            }
            response.setHeader("Content-Disposition", "attachment; filename=\""
                    + fileName + "\"");
            exporter = new XlsExporter(template, response.getOutputStream());
            exporter.setSXSSFWorkbook(true);
            exporter.setRowAccessWindowSize(500);
            exporter.export(data, "Sheet!A1");
            data=null;
        }
        catch (Exception e)
        {
            logger.error("exportXls error", e);
        }
        finally
        {
            exporter.close();
        }
    } 
    
     protected String encodeFileName(String name)
    {
        String agent = getRequest().getHeader("User-Agent");
        if (null == agent)
        {
            return name;
        }

        agent = agent.toLowerCase();
        try
        {
            // for IE browser
            if (agent.indexOf("msie") == -1)
            {
                name = new String(name.getBytes(Charsets.UTF_8),
                        Charsets.ISO_8859_1);
            }
            else
            {
                name = URLEncoder.encode(name, "UTF-8");
                name = name.replace("+", "%20");
            }
        }
        catch (UnsupportedEncodingException e)
        {
            logger.error("encodeFileName error:",e);
        }
        return name;
    }
    
    /**
     * 获取文件名后缀 
     * @param name 文件名称
     * @return
     */
    public static String getFileExt(String name)
    {
        if (name == null)
        {
            return null;
        }

        int pos = name.lastIndexOf(".");
        if (pos == -1)
        {
            return null;
        }

        return name.substring(pos + 1);
    }
    

3.编写测试类

/**
     * 导出excel
     * @param response
     */
    @GetMapping("exportUser")
    public void exportUser(HttpServletResponse response)
    {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("list", initUser());
        //模板路径
        String template = "E:\\template\\xls\\user_list_template.xlsx";
        String fileName = "user_export_" + System.currentTimeMillis();
        exportXls(template, fileName, map, response);
    }
    
    public List<TUser> initUser(){
        List<TUser> tUsers =new ArrayList<TUser>();
        for(int i=1;i<=10;i++)
        {
            TUser tUser =new TUser();
            tUser.setOid(Long.parseLong(String.valueOf(i)));
            tUser.setName("张三"+i);
            tUser.setAge(10+i);
            tUser.setAddress("gz");
            tUsers.add(tUser);
        }
        
        return tUsers;
    }

4.定义模板文件

图片.png

说明:

XLS Area定义标记

XLS Area 是JxlsPlus中的一个重要概念,它代表excel模板中需要被解析的矩形区域,由A1到最后一个单元格表示,有利于加快解析速度。 XLS Area 使用excel注释标注表示,它需要被定义在excel 模板的第一个单元格(A1):

jx:area(lastCell = "D3")

jx:each 用于循环输出数据

jx:each(items="list" var="user" lastCell="D3")

each 可以有如下一些属性:

  • items 上下文中集合的变量名;
  • var 在遍历集合的时候每一条记录的变量名;
  • area 该XLS Command的解析区域;
  • direction 数据在excel中填充的方向,默认(DOWN)向下;
  • select 其值为一个表达式,用来过滤数据。

运行生成结果

图片.png

大数据导出

jxls底层还是使用POI导出excel,用POI导出excel,其中一个关键的问题导出excel占用内存过多,当导出大数据量的时候就会出现内存溢出。POI提供了SXSSFWorkbook来实现大数据量导出,上述的工具类中提供了一个变量是否采用SXSSFWorkbook导出,其中rowAccessWindowSize导出多少行数据进行提交,提升性能,而防止内存溢出的问题。

 is = new FileInputStream(new File(template));
            if (isSXSSFWorkbook)
            {
                book = WorkbookFactory.create(is);
                transformer = PoiTransformer.createSxssfTransformer(book,
                        rowAccessWindowSize, true);
                context = new PoiContext(map);
            }
            else
            {
                transformer = TransformerFactory.createTransformer(is, os);
                context = new Context(map);
            }

其他特性

1.jxls的重要的特性是可以通过定义模板动态的导出,例如上述导出的用户信息,如果我需要新增导出性别一列,只需模板新增一列即可,无需修改任何代码。

2.jxls可以直接将查询的结果集导出到excel中,从而减少了查询数据存放时占用的内存。