Java SpringBoot使用easyExcel,浏览器下载
目录
简单title向下补充表格
like this
创建一个data类(CargoRightsExcelData
),定义每一列需要导出的字段
package com.zmy.agency.cargo.data;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;
@Data
public class CargoRightsExcelData {
// 列宽
@ColumnWidth(24)
// 列的标题
@ExcelProperty({" "})
// 字段数据
private String category;
@ColumnWidth(18)
@ExcelProperty({"在途", "立方数"})
private Double onWayQuantity;
。。。。。。
@ColumnWidth(18)
@ExcelProperty({"合计", "立方米"})
private Double sumQuantity;
@ColumnWidth(18)
@ExcelProperty({"合计", "金额"})
private Double sumAmount;
}
路由层
/**
* Controller文件
* excel导出
*/
// 正常应该用GET请求,我这里需要传的是一个对象,所以用了POST请求
@PostMapping(value = "/excel")
public void statisticsExcel(HttpServletResponse response, @RequestBody CargoRightsStatisticsDto cargoRightsDto) throws UnsupportedEncodingException {
if (cargoRightsDto.getCargoRights() != null || cargoRightsDto.getCargoRights().length > 0) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode(DateUtil.getCurrentDateTimeStr() + "货权统计表", "UTF-8").replaceAll("\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xls");
// 填充数据
marketPriceService.exportExcel(response, cargoRightsDto);
}
}
服务impl层
@Override
public JsonResult exportExcel(HttpServletResponse response, CargoRightsStatisticsDto cargoRights) {
JsonResult result = new JsonResult();
result.setCode(200);
try {
// 拿到需要导出的数据,根据自己的业务逻辑自行编写
List<Map<String, Object>> datas = cargoStatisticsWhole(cargoRights);
// 初始化前面定义的data类为一个数组
List<CargoRightsExcelData> cargoRightsExcelDataList = new ArrayList<>();
// 开始填充data
for (Map<String, Object> data: datas) {
CargoRightsExcelData cargoRightsExcelData = new CargoRightsExcelData();
String keyName = data.get("name") == null ? "未定义" : (String) data.get("name");
String name = " ".repeat((int) data.get("deep")) + keyName;
cargoRightsExcelData.setCategory(name);
cargoRightsExcelData.setOnWayQuantity((Double) data.get("on_way_quantity"));
cargoRightsExcelData.setOnWayAmount((Double) data.get("on_way_amount"));
cargoRightsExcelData.setStockQuantity((Double) data.get("stock_quantity"));
cargoRightsExcelData.setStockAmount((Double) data.get("stock_amount"));
cargoRightsExcelData.setSumQuantity((Double) data.get("sum_quantity"));
cargoRightsExcelData.setSumAmount((Double) data.get("sum_amount"));
cargoRightsExcelDataList.add(cargoRightsExcelData);
// 写入easyExcel流,在这一步,其实表格就已经生成了
EasyExcel.write(
response.getOutputStream(),
CargoRightsExcelData.class
).sheet("货权统计").doWrite(cargoRightsExcelDataList);
return result;
} catch (IOException e) {
result.setCode(406);
result.setMessage(e.getMessage());
return result;
}
}
复杂的模板填充
这也是我特别想记录的
首先,我也是参考别的代码在做
查阅到这一片时,EasyExcel模板填充踩坑, 给我留下的印象就是导出辅助data类,不能用驼峰法
查阅到这一片时,easyExcel使用模板填充式的导出,我照着写了一下
比较有意思的一点就是,两篇,我都只看见了map数据,没有看到初始化辅助data那一行代码(我没注意看,所以我的代码没用到辅助data类,但是,我是写完了,生效了,才发现没用上的,哈啊哈)
所以我的完成过程如下
先准备一个excel导出模板
行数据要用x.字段命名
路由层
/**
* Controller文件
* 导出出入库单据的表格
*/
@GetMapping(value = "/out-in-excel/{id}")
public JsonResult outInExcel(@PathVariable("id") String id, HttpServletResponse response) throws IOException {
if (StringUtils.isBlank(id)){
return JsonResult.error("ID未找到!");
}
OrderRecord order = orderRecordService.getById(id);
if (order == null) {
return JsonResult.error("ID未找到!");
}
String fileName;
if ("inStock".equals(order.getType())) {
fileName = "入库通知单_" + DateUtil.getCurrentDateTimeStr();
} else if ("outStock".equals(order.getType())) {
fileName = "出库通知单_" + DateUtil.getCurrentDateTimeStr();
} else {
return JsonResult.error("错误的数据类型");
}
// 定义文件流
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
String excelFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\+", "%20");
// excelFileName: 文件名
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + excelFileName + ".xls");
OutputStream outputStream = response.getOutputStream();
// 将id和outputStream文件流传入服务层
JsonResult result = orderRecordService.outInExcelById(id, outputStream);
return result;
}
服务层impl
@Override
public JsonResult outInExcelById(String id, OutputStream outputStream) {
// 定义在报错情况下返回的内容
JsonResult result = new JsonResult();
result.setCode(200);
// 拿到需要导出的数据,根据你的业务需求自行处理
OrderRecord order = baseMapper.selectById(id);
try {
// 定位模板文件的base位置
String basePath = PathKit.getWebRootPath() + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator;
// 定位模板文件的最终位置
String templateName;
Map<String, Object> data = new HashMap<>();
if ("inStock".equals(order.getType())) {
templateName = basePath + "templates/in_order_template.xls";
} else if ("outStock".equals(order.getType())) {
templateName = basePath + "templates/out_order_template.xls";
} else {
return JsonResult.error("错误的数据类型");
}
// outputStream文件流写入EasyExcel,使用withTemplate方法调用模板(参数就是模板文件的路径)
ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(templateName).build();
// 创建一个页签
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 写入数据,这里就是只会出现一次的数据
data.put("local_date", order.getOrderDate().toString());
data.put("code", order.getCode());
data.put("custody_display", order.getCustodyDisplay());
data.put("storehouse_display", order.getStorehouseDisplay());
data.put("stockist_display", order.getStockistDisplay());
data.put("pick_up_display", order.getPickUpDisplay());
// 这里定义一个自动填充需要遍历的表格类| NewRow(Boolean.TRUE) 自动换行
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
/**
* 这里比较有意思的 就是,因为第一篇博客的影响,我给表格的字段使用的都是下划线的形式,但是我的数据都是驼峰的形式
* 导致我的数据写不进去,这时我才意识到我写的辅助data类没有到用到,但是我并不想研究怎么用,我就死马当做活马医,
* 试一下我直接在模板里面改成驼峰命名看能不能生效,于是乎,it`s work!
*/
// 设置自动遍历填充,a的取值内容为order.getOrderRecordLines(),这是一个O2M字段的数据
excelWriter.fill(new FillWrapper("a", order.getOrderRecordLines()), fillConfig, writeSheet);
// 将data的数据完整填充进excelWriter
excelWriter.fill(data, writeSheet);
// 关闭文件流
excelWriter.finish();
return result;
} catch (Exception e) {
return JsonResult.error(e.toString());
}
}
结论
成功了!
回过头我真的很好奇我没有用到辅助data类,就又倒回去查看前面的博客
这一片是定义一个不变的map数据,一个是需要便利的数据结合我的代码可以得出结论
可变的遍历数据,如果模板的取值和我的数据一致,可以不用定义辅助data类,Nice!
似乎,我能这样写,还得益于,我的模型定义,外键关联我都会再定义一个显示字段的缘故。
like this:
/**
* 存货方
*/
private String stockistId;
/**
* 存货方Display
*/
@TableField(exist = false)
private String stockistDisplay;
/**
* 指定仓库
*/
private String storehouseId;
/**
* 指定仓库Display
*/
@TableField(exist = false)
private String storehouseDisplay;
还有一个弊端:如果你取出来的数据还需要处理,要么你再在数据层定义一个字段,要么就只能老老实实写辅助data类,先给data辅助,再将data赋值给excelWriter