jdk 1.8前端
Maven 3.6java
Tomcat 8.5web
SpringBoot 2.1.4.RELEASEspring
Apache-POI 3.6sql
Ideaapache
注意: 我是在现有的基于SpringBoot的分模块项目中集成的文件导出功能,因此就再也不从项目构建开始介绍了,若有对SpringBoot项目构建不熟悉,或对构建分模块项目感兴趣的同窗,可移步[SpringBoot构建分模块项目...(还没写呢)]json
在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>
当pom配置好以后,咱们查看右侧Maven,检查是否下载成功;app
有的同窗可能会遇Maven中显示Apache-POI确实已经被引入了,可是仍然在项目中没法使用。其实此时jar确实已经被下载到本地仓库了,只是没有添加到咱们项目中。添加方式参照[解决Idea项目启动报错:程序包javax.servlet.http不存在],只是此时的jar来源再也不是Tomcat目录下,而是咱们的本地仓库;dom
前面都作好的话,就能够编写咱们的代码了;
先说下我设计这个接口以前想到的问题,以及对该接口的应用场景
该工具类设计好以后,可对本项目提供服务,也可包装成接口,对外暴露服务,调用者只需按照咱们要求的参数格式给出参数便可
该项目部署以后,未来其余项目须要用到导出功能,只需调用咱们的接口便可,
根据以上两点,在工具类的设计上要遵循低耦合的原则,使其高可用、高复用
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; } }
接口为传统的接口
在业务层对查询到的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); } }
package com.wayne.service; import com.wayne.common.dto.ResponseBean; /** * @author Wayne * @date 2019/5/15 */ public interface ExportService { ResponseBean exportExcel(String request); }
Mapper没什么好贴出来的吧~~~
<!-- 此查询方法是从视图中查,目的仍是为了下降耦合性,在视图中已经对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异常
前端代码就再也不展现了,由于此功能在前端只是一个按钮和点击事件
由于不肯定时间在第几列,因此没法动态将时间列设置为日期格式,须要后续手动处理
开发怎能不留扩展字段 (¬_¬)…