欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java无模板导出Excel,Apache-POI插件实现

程序员文章站 2022-04-28 14:18:45
开发环境 jdk 1.8 Maven 3.6 Tomcat 8.5 SpringBoot 2.1.4.RELEASE Apache-POI 3.6 Idea 注意: 我是在现有的基于SpringBoot的分模块项目中集成的文件导出功能,所以就不再从项目构建开始介绍了,如有对SpringBoot项目构 ......

 

开发环境

  1. jdk 1.8

  2. maven 3.6

  3. tomcat 8.5

  4. springboot 2.1.4.release

  5. apache-poi 3.6

  6. idea

注意: 我是在现有的基于springboot的分模块项目中集成的文件导出功能,所以就不再从项目构建开始介绍了,如有对springboot项目构建不熟悉,或对构建分模块项目感兴趣的同学,可移步[springboot构建分模块项目...(还没写呢)]

maven依赖

  1. 在pom.xml中添加依赖,一下两种方式,根据个人习惯,任选其一;

 
<!-- 引入方式一 -->
<properties>
     <!-- ↓↓↓↓↓↓
        此处省略项目中用到的基本jar包的版本
    ↑↑↑↑↑↑-->   
    <!-- 版本 -->
    <apache.poi.version>3.6</apache.poi.version>
</properties>
​
​
<dependencies>
    <!-- ↓↓↓↓↓↓
        此处省略项目中用到的基本jar包
     ↑↑↑↑↑↑-->
​
    <!-- apache-poi -->
    <dependency>
        <groupid>org.apache.poi</groupid>
        <artifactid>poi</artifactid>
        <version>${apache.poi.version}</version>
    </dependency>
</dependencies>
<!-- 引入方式二 -->
<dependencies>
    <!-- ↓↓↓↓↓↓
        此处省略项目中用到的基本jar包
     ↑↑↑↑↑↑-->
​
    <!-- apache-poi -->
    <dependency>
        <groupid>org.apache.poi</groupid>
        <artifactid>poi</artifactid>
        <version>3.6</version>
    </dependency>
</dependencies>
  1. 当pom配置好之后,我们查看右侧maven,检查是否下载成功;

  2. 有的同学可能会遇maven中显示apache-poi确实已经被引入了,但是仍然在项目中无法使用。其实此时jar确实已经被下载到本地仓库了,只是没有添加到我们项目中。添加方式参照[解决idea项目启动报错:程序包javax.servlet.http不存在],只是此时的jar来源不再是tomcat目录下,而是我们的本地仓库;

  3. 前面都做好的话,就可以编写我们的代码了;

工具类

先说下我设计这个接口之前想到的问题,以及对该接口的应用场景

  1. 该工具类设计好之后,可对本项目提供服务,也可包装成接口,对外暴露服务,调用者只需按照我们要求的参数格式给出参数即可

  2. 该项目部署之后,将来其他项目需要用到导出功能,只需调用我们的接口即可,

根据以上两点,在工具类的设计上要遵循低耦合的原则,使其高可用、高复用

package com.wayne.utils;
​
import com.alibaba.fastjson.json;
import com.google.common.collect.lists;
import com.wayne.common.exception.serviceexception;
import org.apache.poi.hssf.usermodel.*;
​
import java.io.file;
import java.io.fileoutputstream;
import java.util.list;
import java.util.map;
​
/**
 * 文件导出工具类
 * @author wayne
 * @date 2019/5/15
 */
public class exportutil {
​
    /**
     * 无模块导出excel方法,
     * 参数data格式: [
     *      {
     *          "姓名": "张三";
     *          "年龄": "23";
     *          "性别": "男"
     *      },
     *      {
     *          "姓名": "李四";
     *          "年龄": "24";
     *          "性别": "男"
     *      }
     * ]
     *
     * @param data 需要导出的数据
     * @param filename 导出后保存到本地的文件名
     * @return 创建好的excel文件,用于前端下载
     * @throws serviceexception 自定义runtimeexception子类异常
     */
    public static hssfworkbook exportexcel(list<map<string, object>> data, string filename) throws serviceexception {
        // 从参数data中解析出打印的每列标题,放入title中
        list<string> title = lists.newarraylist();
        for(map.entry<string, object> entry : data.get(0).entryset()) {
            title.add(entry.getkey());
        }
        // 新建一个excel文件
        hssfworkbook wb = new hssfworkbook();
        // excel中的sheet
        hssfsheet sheet = wb.createsheet();
        // sheet中的行,0表示第一行
        hssfrow row = sheet.createrow(0);
        // 设置标题居中
        hssfcellstyle cellstyle = wb.createcellstyle();
        cellstyle.setalignment(hssfcellstyle.align_center);
        // sheet中的单元格
        hssfcell cell = null;
​
        // 给第一行赋值,值为我们从参数中解析出的标题,因此需要我们在传参的时候需要严格按照约定
        for(int i = 0; i < title.size(); i++) {
            cell = row.createcell(i);
            cell.setcellvalue(title.get(i));
            cell.setcellstyle(cellstyle);
        }
​
       // 根据参数内容数量,创建表格行数
        for(int i = 0; i < data.size(); i++) {
            row = sheet.createrow(i + 1);
​
            map<string, object> values = data.get(i);
​
            // 将参数插入每一个单元格
            for(int k = 0; k < title.size(); k++) {
                object value = values.get(title.get(k));
                if(null == value) {
                    value = "";
                }
                string val = json.tojsonstring(value);
                row.createcell(k).setcellvalue(val);
            }
        }
​
        // 将文件写到硬盘中,将来根据需求,或写到服务器中,因此在实际开发中,最好将"e:/temp/"配置在.properties配置文件中,仪表项目上线事更换方便
        try {
            fileoutputstream fileoutputstream = new fileoutputstream(new file("e:/temp/" + filename));
            wb.write(fileoutputstream);
            fileoutputstream.flush();
            fileoutputstream.close();
        } catch (exception e) {
            throw new serviceexception("文件导出失败");
        }
        return wb;
    }
}

接口层和业务层

  1. 接口为传统的接口

  2. 在业务层对查询到的data做了处理,已达到符合工具类规范

package com.wayne.impl;
​
import com.alibaba.fastjson.json;
import com.wayne.common.dto.responsebean;
import com.wayne.common.entity.cmsuser;
import com.wayne.mapper.cmsusermapper;
import com.wayne.service.exportservice;
import com.wayne.utils.exportutil;
import org.apache.poi.hssf.usermodel.hssfworkbook;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
​
import java.util.list;
import java.util.map;
import java.util.uuid;
​
/**
 * 文件导出service实现类
 * @author wayne
 * @date 2019/5/15
 */
@service
public class exportserviceimpl implements exportservice {
​
    @autowired
    private cmsusermapper usermapper;
​
    /**
     * 无模板导出excel
     * @param request 前端传来参数,格式为json字符串的对象,因为在实际开发中可能要用到根据查询参数导出,比如: 导出年龄在18 --- 26之间的、性别为女的用户
     * @return
     */
    @override
    public responsebean exportexcel(string request) {
        responsebean responsebean;
​
        // 将前端参数转换为对象
        cmsuser user = json.parseobject(request, cmsuser.class);
        // 根据条件查询需要导出的数据(后附sql内容)
        list<map<string, object>> userlist = usermapper.selectbyparams(user);
​
        try {
            // 生成文件名
            string filename = uuid.randomuuid() + ".xls";
            // 调用工具类生成excel,此时接收到的wb即是完整的excel
            hssfworkbook wb = exportutil.exportexcel(userlist, filename);
            //todo 此处可根据实际需要添加是否写到前端,供用户下载
            responsebean = responsebean.createinstance(boolean.true, "导出成功", filename);
        } catch (exception e) {
            responsebean = responsebean.createinstance(false, 500, e.getmessage());
            system.out.println(e.getmessage());
        }
        return responsebean;
    }
}

 

package com.wayne.web.comtroller;
​
import com.wayne.common.dto.responsebean;
import com.wayne.service.exportservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.postmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;
​
/**
 * 文件导出接口
 * 接口层不做业务处理
 * @author wayne
 * @date 2019/5/15
 */
@restcontroller
@requestmapping("/export")
public class exportcontroller {
​
    @autowired
    private exportservice exportservice;
​
    /**
     * 导出excel
     * @param request 导出条件
     */
    @postmapping("/excel")
    public responsebean exportexcel(string request) {
        return exportservice.exportexcel(request);
    }
}

其他区相关代码

service

package com.wayne.service;
​
import com.wayne.common.dto.responsebean;
​
/**
 * @author wayne
 * @date 2019/5/15
 */
public interface exportservice {
    responsebean exportexcel(string request);
}

mapper

mapper没什么好贴出来的吧~~~

mapper.xml

<!-- 此查询方法是从视图中查,目的还是为了降低耦合性,在视图中已经对data做了格式调整 -->
<select id="selectbyparams" resulttype="map">
    select
      *
    from
      v_user
    where
      `用户名` != 'admin'
    <where>
      <if test="username != null and username != ''">
        and `用户名` = #{username, jdbctype=varchar}
      </if>
      <if test="mobile != null and mobile != ''">
        and `手机号` = #{mobile,jdbctype=varchar}
      </if>
    </where>
    order by `用户名`
  </select>

视图

select
    `cms_user`.`username` as `用户名`,
    `cms_user`.`realname` as `真实姓名`,
    `cms_user`.`sex` as `性别`,
    `cms_user`.`email` as `邮箱`,
    `cms_user`.`mobile` as `手机号`,
    `cms_user`.`status` as `状态`,
    `cms_user`.`create_time` as `创建时间`,
    `cms_user`.`create_user_id` as `创建者`
from
    `cms_user`

其它

本项目中用到的responsbean为自定义返回到前端的实体类,serviceexception为自定义runtimeexception异常

前端代码就不再展示了,因为此功能在前端只是一个按钮和点击事件

效果展示

Java无模板导出Excel,Apache-POI插件实现

Java无模板导出Excel,Apache-POI插件实现

因为不确定时间在第几列,所以无法动态将时间列设置为日期格式,需要后续手动处理

Java无模板导出Excel,Apache-POI插件实现

预留占位

开发怎能不留扩展字段 (¬_¬)…