• SpingBoot整合jxls2.0-excel导出—— 列表循环,自定义方法,超链接等


           Java中实现excel导出数据的方法有很多,一般简单的可以通过操作POI进行,但是复杂的excel格式导出如果用POI就显得非常麻烦,本文介绍的jxls2.0完全依据模板进行导出,只需要进行简单的配置加数据,即可导出人工处理级别的复杂excel格式,因此写下几个系列教程供大家参考。

           普通循环列表导出是一个非常简单的需求,实现它的方式有两种Api

    方式一:jxls-core依赖(功能较弱,不推荐)

    <dependency>
        <groupId>net.sf.jxls</groupId>
        <artifactId>jxls-core</artifactId>
        <version>1.0.5</version>
    </dependency>

    另外需要注意一点:很多同学在做模板类型数据导出的时候,频繁遇到导出后文件格式打不开的情况,这个时候最有可能导致的错误不是代码有问题,而是因为模板文件被编译成了二进制文件,因此对于这一点需要特别处理,现在大多都是用Maven作为管理工具,对于Maven需要特别的配置如下:

    <build>
       <plugins>
          <!-- 对xls模板打包但不编译 -->
          <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-resources-plugin</artifactId>
             <configuration>
                <nonFilteredFileExtensions>
                   <nonFilteredFileExtension>xls</nonFilteredFileExtension>
                </nonFilteredFileExtensions>
             </configuration>
          </plugin>
       </plugins>
    </build>
    

    是否是这个错误也很好检查,办法如下

    手动打开target包下对应的模板文件,看是否会报格式错误,自然就会明白问题到底出在哪一步了

    具体实现代码非常简单,就直接贴出来了:

        /***
         * 数据导出
         * @param request
         * @param model
         * @return
         */
        @RequestMapping(value = "/monitoring/exportExcel")
        public void exportExcel(HttpServletRequest request, HttpServletResponse response, HttpSession session, Model model) throws Exception {
            
            // List集合 此处省略
            List<Test> test = new ArrayList<>();
            
            // 加入到模板集合中
            Map<String, Object> datamap = new HashMap<>();
            datamap.put("test", test);
    
            try {
                // 获取模板绝对路径
                String path = session.getServletContext().getRealPath("");
                // 模板文件路径
                String srcPath = path + "/" + Config.MONITOR_EXPORT_TEMPLATE;
    
                // 设置jxls内置
                Configuration config = new Configuration();
                config.setMetaInfoToken("\\");
                XLSTransformer transformer = new XLSTransformer(config);
    
                // 取得文件名。
                String fileName = DateUtils.getTime3(endtime) + ".xls";
    
                response.reset();
                response.setHeader("Accept-Ranges", "bytes");
                /* 解决各浏览器的中文乱码问题 */
                String userAgent = request.getHeader("User-Agent");
    
                // fileName.getBytes("UTF-8")处理safari的乱码问题
                byte[] bytes = userAgent.contains("MSIE") ? fileName.getBytes() : fileName.getBytes("UTF-8");
    
                // 各浏览器基本都支持ISO编码
                fileName = new String(bytes, "ISO-8859-1");
                response.setHeader("Content-disposition", String.format("attachment; filename="%s"", fileName));
                response.setContentType("application/octet-stream;charset=UTF-8");
    
                // 利用poi完善excel中未填充的数据
                HSSFWorkbook workBook = null;
                InputStream ins = null;
                OutputStream out = null;
                try {
                    ins = new BufferedInputStream(new FileInputStream(srcPath));
                    workBook = (HSSFWorkbook) transformer.transformXLS(ins, datamap);
                    out = response.getOutputStream();
                    // 将内容写入输出流并把缓存的内容全部发出去
                    workBook.write(out);
                    out.flush();
                } catch (Exception e) { } finally {
                    try {
                        if (ins != null) {
                            ins.close();
                        }
                        if (out != null) {
                            out.close();
                        }
                    } catch (Exception e) {}
                }
            } catch(Exception e) {}
        }

    对应的模板如下:

    注意:代码中集合避免暴露过多信息因此是随意写的,读者真正使用时注意替换参数即可,模板语法类似jstl

    方式二:jxls,jxls-poi依赖

    同样需要注意模板文件地址被编译成二进制的问题,此方案就是我应用于SpringBoot的方案,且此方案的拓展性非常强

    核心依赖:

                    <!-- 用以下版本jxls-api 功能更加强大,解决超链接等特殊需求 -->
    		<dependency>
    			<groupId>org.jxls</groupId>
    			<artifactId>jxls-poi</artifactId>
    			<version>1.0.15</version>
    			<exclusions><!-- 去掉默认日志打印 -->
    				<exclusion>
    					<groupId>ch.qos.logback</groupId>
    					<artifactId>logback-classic</artifactId>
    				</exclusion>
    				<exclusion>
    					<groupId>ch.qos.logback</groupId>
    					<artifactId>logback-core</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>
    
    		<dependency>
    			<groupId>org.jxls</groupId>
    			<artifactId>jxls</artifactId>
    			<version>2.4.6</version>
    			<exclusions><!-- 去掉默认日志打印 -->
    				<exclusion>
    					<groupId>ch.qos.logback</groupId>
    					<artifactId>logback-classic</artifactId>
    				</exclusion>
    				<exclusion>
    					<groupId>ch.qos.logback</groupId>
    					<artifactId>logback-core</artifactId>
    				</exclusion>
    			</exclusions>
    		</dependency>

    注意由于该api自身有日志输出相关的依赖,和我项目本身冲突了,所以我禁用了其日志相关的配置,如果和读者不冲突的话,可以开启,这个点影响不大

    核心方法如下:

    此处我采用的功能有:循环列表,自定义方法(数值转换),超链接等等

    package cn.testin.monitorproxy.util;
    
    import cn.testin.common.util.log.Logit;
    import cn.testin.monitorproxy.pojo.enums.AlarmStatusEnum;
    import cn.testin.monitorproxy.pojo.enums.MonitorTypeEnum;
    import org.jxls.area.Area;
    import org.jxls.builder.AreaBuilder;
    import org.jxls.builder.xls.XlsCommentAreaBuilder;
    import org.jxls.common.CellRef;
    import org.jxls.common.Context;
    import org.jxls.expression.JexlExpressionEvaluator;
    import org.jxls.transform.Transformer;
    import org.jxls.transform.poi.WritableCellValue;
    import org.jxls.transform.poi.WritableHyperlink;
    import org.jxls.util.TransformerFactory;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.text.DecimalFormat;
    import java.text.SimpleDateFormat;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * ******************************
     * Author:      柯贤铭
     * CreateTime:   2019/2/15 14:52
     * Description:  excel导出工具类
     * Version:      V1.0
     * ******************************
     */
    public class ExcelUtils {
    
        /***
         * excel导出公共方法
         * @param fileName      导出文件名
         * @param templateFile  模板文件地址
         * @param list          数据集合
         * @param response      response
         * @param request       request
         */
        public static void exportExcel(String fileName, InputStream templateFile, List<?> list,
                                       HttpServletResponse response, HttpServletRequest request) {
            response.reset();
            response.setHeader("Accept-Ranges", "bytes");
            OutputStream os = null;
            try {
                // 解决各浏览器的中文乱码问题
                String userAgent = request.getHeader("User-Agent");
    
                // fileName.getBytes("UTF-8")处理safari的乱码问题
                byte[] bytes = userAgent.contains("MSIE") ? fileName.getBytes() : fileName.getBytes("UTF-8");
    
                // 各浏览器基本都支持ISO编码
                fileName = new String(bytes, "ISO-8859-1");
                response.setHeader("Content-disposition", String.format("attachment; filename="%s"", fileName));
                response.setContentType("application/octet-stream;charset=UTF-8");
    
                Context context = new Context();
    
                //设置参数变量
                context.putVar("list", list);
    
                Map<String , Object> myFunction = new HashMap<>();
                myFunction.put("my", new ExcelUtils());
    
                os = response.getOutputStream();
    
                // 启动新的jxls-api 加载自定义方法
                Transformer trans = TransformerFactory.createTransformer(templateFile, os);
                JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator) trans.getTransformationConfig().getExpressionEvaluator();
                evaluator.getJexlEngine().setFunctions(myFunction);
    
                // 载入模板、处理导出
                AreaBuilder areaBuilder = new XlsCommentAreaBuilder(trans);
                List<Area> areaList = areaBuilder.build();
                areaList.get(0).applyAt(new CellRef("Hello!A1"), context);
                trans.write();
            } catch (Exception e) {
                Logit.errorLog(e.getMessage(), new Throwable(e));
            } finally {
                try {
                    if (os != null) {
                        os.flush();
                        os.close();
                    }
    
                    if (templateFile != null) {
                        templateFile.close();
                    }
                } catch (Exception e) {
                    Logit.errorLog(e.getMessage(), new Throwable(e));
                }
            }
        }
    
        // 格式化时间
        public Object formatDate(Long time){
            if(time != null){
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String dateStr = sdf.format(time);
                return dateStr;
            }
            return "--";
        }
    
        // 时延-处理执行时间 ms -> s 且保留两位
        public Object timeChange(Long time){
            if(time != null){
                DecimalFormat df = new DecimalFormat("0.00");
                String result = df.format((double)time / 1000);
                return result.equals("0.00") ? "--" : result + "s";
            }
            return "--";
        }
    
        // 超链接方法
        public WritableCellValue myLink(String address, String title) {
            return new WritableHyperlink(address, title);
        }
    
        // 告警类型转换 -> MonitorTypeEnum枚举类
        public Object alarmType(Integer type){
            if(type != null){
                return MonitorTypeEnum.valueOf(type).getLabel();
            }
            return "--";
        }
    
        // 告警状态转换 -> AlarmStatusEnum枚举类
        public Object statusType(Integer type){
            if(type != null){
                return AlarmStatusEnum.valueOf(type).getLabel();
            }
            return "--";
        }
    }
    

    模板详情如下:

    最终效果:

    希望大家可以从我的博客里找到一点关于Excel导出的办法吧~

  • 相关阅读:
    Vue.js —— 关闭eslint校验
    彻底搞懂 module.exports/exports/import/export/export default
    JS 中的原型和原型链
    TypeScript-初级-08-声明合并
    TypeScript-初级-07-泛型
    TypeScript-初级-06-类与接口
    TypeScript-初级-05-类
    TypeScript-初级-04-枚举
    TypeScript-初级-03-元组
    TypeScript-初级-02-字符串字面量类型
  • 原文地址:https://www.cnblogs.com/kkzhilu/p/12859513.html
Copyright © 2020-2023  润新知