【前提提要】
在项目中客户提出了导出的特定格式,所以 Hutool 工具就没那么方便了,这次使用了 JXLS 工具。为了便于理解,本篇文章直接使用实例来说明。
【实例】
1、首先引入 jxls 依赖
<!-- JXLS 账票导出 -->
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-poi</artifactId>
<version>2.10.0</version>
</dependency>
2、创建一个工具类
用于接收导出数据、写入文件及导出文件。
工具类 JXLSExcel.java
package com.example.hellospringboot.Tools;
import org.jxls.common.Context;
import org.jxls.transform.Transformer;
import org.jxls.transform.poi.PoiTransformer;
import org.jxls.util.JxlsHelper;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
/**
* Excel 帐票输出
* @author admin
*/
@Component
public class JXLSExcel {
/**
* Excel 账票模板出力
*
* @param outStream 输出流
* @param templateResource 模板文件
* @param data 帐票数据
*/
public IOException createDocument(OutputStream outStream, Resource templateResource, Map<String, Object> data) {
IOException e = null;
try (InputStream input = templateResource.getInputStream()) {
Context context = new Context();
for (Map.Entry<String, Object> element : data.entrySet()) {
context.putVar(element.getKey(), element.getValue());
}
Transformer transformer = PoiTransformer.createTransformer(input, outStream);
transformer.setFullFormulaRecalculationOnOpening(true);
JxlsHelper.getInstance().processTemplate(context,transformer);
} catch (IOException exception) {
e = exception;
} catch (RuntimeException exception) {
e = new IOException(exception);
} finally {
closeAndFlushOutput(outStream);
}
return e;
}
/**
* 保存并关闭输出流
*
* @param outStream 输出流
*/
private void closeAndFlushOutput(OutputStream outStream) {
try {
outStream.flush();
outStream.close();
} catch (IOException exception) {
// ignore exception
}
}
}
3、创建一个导出文件的模板
- 调整好表格样式
- 定义导出数据的属性名
- 通过表格的批注声明一些 jxls 配置
如图,
1 是声明出表格中最后一个表格的位置(g是列,4是行),要在第一个表格添加批注声明;
2 是声明了需要循环的数据和表格位置,items 声明循环的数据是 datas,var 声明循环到当前的 item 是 data,lastCell 声明了循环的范围,在需要表格循环的第一个表格位置添加批注声明;
3 是属于循环外的数据,所以要额外定义,所以这个表格需要传入的数据是 datas 和 sum 。
PS:添加批注的方式是 审阅 -> 新建批注
4、传入数据
PersonServiceImpl.java
/**
* 导出
* @param response
* @param personQueryDto 查询条件
* @throws IOException
*/
@Override
public void export(HttpServletResponse response, PersonQueryDto personQueryDto) throws IOException {
// 这里可以根据查询条件查询数据,不是重点,暂不延伸
// 注意属性名要与表格中的一一对应,对应不上的会显示空
List<Person> list = personMapper.selectList(new QueryWrapper<>());
response.setContentType("application/octet-stream;charset=utf-8");
response.setHeader(org.springframework.http.HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.builder("attachment").filename("SubMaterialTemplate.xlsx", StandardCharsets.UTF_8).build().toString());
// 定义一个 map 用于传导出数据,注意 key 要用 datas 和 sum
Map<String, Object> data = new HashMap<>(2);
data.put("datas", list);
data.put("sum", list.size());
IOException exception = jxlsExcel.createDocument(response.getOutputStream(), templateExcel, data);
if (exception != null) {
//异常场合
throw exception;
}
}
PS:因为我的例子是在前台下载出文件,所以是以上写法。在 JXLS 官方文档中是在后台文件下指定下载,只要将传入的 OutputStream 重新定义一下即可。
/**
* 导出
* @param response
* @param personQueryDto 查询条件
* @throws IOException
*/
@Override
public void export(HttpServletResponse response, PersonQueryDto personQueryDto) throws IOException {
// 这里可以根据查询条件查询数据,不是重点,暂不延伸
// 注意属性名要与表格中的一一对应,对应不上的会显示空
List<Person> list = personMapper.selectList(new QueryWrapper<>());
// 定义一个 map 用于传导出数据,注意 key 要用 datas 和 sum
Map<String, Object> data = new HashMap<>(2);
data.put("datas", list);
data.put("sum", list.size());
// 指定下载到 target 下,文件名为人员导出
OutputStream os = new FileOutputStream("target/人员导出.xlsx");
IOException exception = jxlsExcel.createDocument(os, templateExcel, data);
if (exception != null) {
//异常场合
throw exception;
}
}
5、PersonController.java
/**
* 导出
*/
@PostMapping(value = "/export")
public void export(@RequestBody @Validated PersonQueryDto personQueryDto, HttpServletResponse response) throws IOException {
personService.export(response, personQueryDto);
}
6、前台下载
jxlsExport () {
this.$axios.post('/person/export', {}, {
responseType: 'arraybuffer'
}).then((res) => {
this.fileSaveAs(res, '人员一览表.xlsx')
})
},
/**
* 文件另存为
* @param res 文件下载服务器响应response
* @param fileName 存储的文件名
*/
fileSaveAs (res, fileName) {
const doc = document
const url = window.URL.createObjectURL(new Blob([res]))
const link = doc.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName)
doc.body.appendChild(link)
link.click()
doc.body.removeChild(link)
}
7、下载效果
下载到前台的效果:
下载到后台的效果:
两种方式下载的文件内容是相同的。
【官网地址】
有问题欢迎留言~