需求
以指定格式导出数据,以方便运营人员后续修改后直接导入。
格式大概这样:
搜了下发现用EasyExcel的填充功能可以完美实现,导出后的格式与模板完全一致,不需要另外配置,只需要设置需要填充的部分,非常简单。
实现
模板
{name}是普通变量,调用时用Map填充
{.name}是列表变量,调用时用List填充
工具类
@Slf4j
public class EasyExcelUtils {
/**
* 按照模板导出Excel
*
* @param response
* @param tClass
* @param templateFileName
* @param fileName
* @param list
* @param headVariableMap
* @param <T>
* @throws Exception
*/
public static <T> void writeExcelWithTemplate(HttpServletResponse response, Class<T> tClass, String templateFileName, String fileName,
List<T> list, Map<String, Object> headVariableMap) throws Exception {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
//使用swagger可能会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 分多次 填充 会使用文件缓存(省内存)
try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
//填充普通变量
excelWriter.fill(headVariableMap, writeSheet);
//填充list变量
excelWriter.fill(list, writeSheet);
excelWriter.finish();
}
}
调用工具类
//模板文件
String templateFileName = "./consignment_return_export_template.xlsx";
//导出文件名称
String fileName = "退供确认导入模板";
//主体信息
Map<String, Object> headVariableMap = MapUtils.newHashMap();
headVariableMap.put("orderDate", OrderUtils.convertTime(System.currentTimeMillis(), "yyyy-MM-dd"));
List<PlatformJdReturnExportExcelDTO> list = new ArrayList<>();
//这里随便写点数据用于测试
PlatformJdReturnExportExcelDTO dto = new PlatformJdReturnExportExcelDTO();
list.add(dto);
EasyExcelUtils.writeExcelWithTemplate(response, PlatformJdReturnExportExcelDTO.class, templateFileName, fileName, list, headVariableMap);
参考的EasyExcel官方示例
示例代码1
/**
* 最简单的填充
*
* @since 2.1.1
*/
@Test
public void simpleFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
String templateFileName =
TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx";
// 方案1 根据对象填充
String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx";
// 这里 会填充到第一个sheet, 然后文件流会自动关闭
FillData fillData = new FillData();
fillData.setName("张三");
fillData.setNumber(5.2);
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);
// 方案2 根据Map填充
fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx";
// 这里 会填充到第一个sheet, 然后文件流会自动关闭
Map<String, Object> map = MapUtils.newHashMap();
map.put("name", "张三");
map.put("number", 5.2);
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
}
示例代码2
/**
* 填充列表
*
* @since 2.1.1
*/
@Test
public void listFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// 填充list 的时候还要注意 模板中{.} 多了个点 表示list
// 如果填充list的对象是map,必须包涵所有list的key,哪怕数据为null,必须使用map.put(key,null)
String templateFileName =
TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx";
// 方案1 一下子全部放到内存里面 并填充
String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx";
// 这里 会填充到第一个sheet, 然后文件流会自动关闭
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data());
// 方案2 分多次 填充 会使用文件缓存(省内存)
fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx";
try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
excelWriter.fill(data(), writeSheet);
excelWriter.fill(data(), writeSheet);
}
}
过程中遇到的问题:
不想在磁盘上保留导出文件
官方的例子是输出到本地文件的,去网上搜了一圈也都是保存下来以后再去读取放到Response里,不想那么复杂,为此还去翻了翻源码,结果发现已经有封装好的函数可以直接调,快乐。
//这样是输出到本地文件
ExcelWriter excelWriter = EasyExcel.write(fileName);
//直接输出流
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream());
导出的文件格式不对
去网上查了一下才发现,response 配置写错了,这里不多说
导出的中文文件名乱码
//这个设置只对文件内容有效,
response.setCharacterEncoding("utf-8");
//文件名需要再单独设置一下 URLEncoder.encode(fileName, "UTF-8")
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
发到dev环境后放在项目根目录下的模板文件无法读取
改把模板文件放到OSS
然后改从OSS url读取:
public static <T> void writeExcelWithTemplate(HttpServletResponse response, Class<T> tClass, String templateFileUrl, String fileName,
List<T> list, Map<String, Object> headVariableMap) throws Exception {
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
//使用swagger可能会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
//读取模板文件
URL url = new URL(templateFileUrl);
URLConnection conn = url.openConnection();
conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36");
InputStream templateInputStream = conn.getInputStream();
// 分多次 填充 会使用文件缓存(省内存)
try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(templateInputStream).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
//填充普通变量
excelWriter.fill(headVariableMap, writeSheet);
//填充list变量
excelWriter.fill(list, writeSheet);
excelWriter.finish();
}
}