一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
简介
在业务系统开发中经常会遇到一些报表功能,其中包含excel导出,在java领域中成熟的excel导出实现方案有poi、jxl、esayExcel等,今天给大家介绍jxls通过模板方式导出excel。
Jxls是一个小型Java库,可以轻松生成Excel报告。Jxls使用Excel模板中的特殊标记来定义输出格式和数据布局。
特性
- XML和二进制Excel格式输出(取决于底层Java到Excel的实现)
- 按行和列输出的Java集合
- 条件输出
- 报表定义标记中的表达式语言
- 多页输出
- 原生Excel公式
- 参数化公式
- 分组支持
- 合并单元格支持
- 用于调整excel生成的区域侦听器
- 命令定义的Excel注释标记
- 命令定义的XML标记
- 自定义命令定义
- 流媒体可实现快速输出和更少的内存消耗
- 为所选图纸流式处理(选择图纸流式处理)
官网地址: jxls.sourceforge.net/
实现步骤
1.引入相关jar包
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-poi</artifactId>
<version>1.0.11</version>
</dependency>
2.封装核心导出工具类
private String template;
private boolean hideTemplateSheet = false;
private boolean deleteTemplateSheet = true;
private boolean processFormulas = true;
private boolean useFastFormulaProcessor = true;
private int rowAccessWindowSize = 100;
private boolean isSXSSFWorkbook = false;
private AreaBuilder areaBuilder = new XlsCommentAreaBuilder();
protected Writer writer;
protected OutputStream os;
//省略get、set方法
public void export(Map<String, Object> map, String shirtName)
{
logger.info("export excel begin...");
InputStream is = null;
Workbook book = null;
Transformer transformer = null;
Context context = null;
try
{
is = new FileInputStream(new File(template));
if (isSXSSFWorkbook)
{
book = WorkbookFactory.create(is);
transformer = PoiTransformer.createSxssfTransformer(book,
rowAccessWindowSize, true);
context = new PoiContext(map);
}
else
{
transformer = TransformerFactory.createTransformer(is, os);
context = new Context(map);
}
JexlExpressionEvaluator evaluator = (JexlExpressionEvaluator) transformer
.getTransformationConfig().getExpressionEvaluator();
// 设置静默模式,不报警告
evaluator.getJexlEngine().setSilent(true);
areaBuilder.setTransformer(transformer);
List<Area> xlsAreaList = areaBuilder.build();
if (xlsAreaList.isEmpty())
{
logger.error("No XlsArea were detected for this processing");
return;
}
Area xlsArea = xlsAreaList.get(0);
CellRef targetCellRef = new CellRef(shirtName);
xlsArea.applyAt(targetCellRef, context);
if (processFormulas)
{
setFormulaProcessor(xlsArea);
xlsArea.processFormulas();
}
String sourceSheetName = xlsArea.getStartCellRef().getSheetName();
if (!sourceSheetName.equalsIgnoreCase(targetCellRef.getSheetName()))
{
if (hideTemplateSheet)
{
transformer.setHidden(sourceSheetName, true);
}
if (deleteTemplateSheet)
{
transformer.deleteSheet(sourceSheetName);
}
}
if (transformer instanceof PoiTransformer)
{
((PoiTransformer) transformer).getWorkbook().write(os);
}
else
{
transformer.write();
}
}
catch (Exception e)
{
logger.error("xls export error", e);
}
finally
{
if(is!=null)
{
try
{
is.close();
}
catch (IOException e)
{
logger.error("close io error",e);
}
}
}
logger.info("export excel finished");
}
/**
* 导出excel
*
* @param template
* 模板文件路径
* @param fileName
* 导出文件名称
* @param data
* 数据集合
* @param response
* reponse响应
*/
protected void exportXls(String template, String fileName,
Map<String, Object> data, HttpServletResponse response)
{
logger.info("excel template:" + template);
String ext =getFileExt(template);
XlsExporter exporter = null;
try
{
fileName = encodeFileName(fileName);
if (ext != null)
{
fileName += "." + ext;
}
response.setHeader("Content-Disposition", "attachment; filename=\""
+ fileName + "\"");
exporter = new XlsExporter(template, response.getOutputStream());
exporter.setSXSSFWorkbook(true);
exporter.setRowAccessWindowSize(500);
exporter.export(data, "Sheet!A1");
data=null;
}
catch (Exception e)
{
logger.error("exportXls error", e);
}
finally
{
exporter.close();
}
}
protected String encodeFileName(String name)
{
String agent = getRequest().getHeader("User-Agent");
if (null == agent)
{
return name;
}
agent = agent.toLowerCase();
try
{
// for IE browser
if (agent.indexOf("msie") == -1)
{
name = new String(name.getBytes(Charsets.UTF_8),
Charsets.ISO_8859_1);
}
else
{
name = URLEncoder.encode(name, "UTF-8");
name = name.replace("+", "%20");
}
}
catch (UnsupportedEncodingException e)
{
logger.error("encodeFileName error:",e);
}
return name;
}
/**
* 获取文件名后缀
* @param name 文件名称
* @return
*/
public static String getFileExt(String name)
{
if (name == null)
{
return null;
}
int pos = name.lastIndexOf(".");
if (pos == -1)
{
return null;
}
return name.substring(pos + 1);
}
3.编写测试类
/**
* 导出excel
* @param response
*/
@GetMapping("exportUser")
public void exportUser(HttpServletResponse response)
{
Map<String, Object> map = new HashMap<String, Object>();
map.put("list", initUser());
//模板路径
String template = "E:\\template\\xls\\user_list_template.xlsx";
String fileName = "user_export_" + System.currentTimeMillis();
exportXls(template, fileName, map, response);
}
public List<TUser> initUser(){
List<TUser> tUsers =new ArrayList<TUser>();
for(int i=1;i<=10;i++)
{
TUser tUser =new TUser();
tUser.setOid(Long.parseLong(String.valueOf(i)));
tUser.setName("张三"+i);
tUser.setAge(10+i);
tUser.setAddress("gz");
tUsers.add(tUser);
}
return tUsers;
}
4.定义模板文件
说明:
XLS Area定义标记
XLS Area 是JxlsPlus中的一个重要概念,它代表excel模板中需要被解析的矩形区域,由A1到最后一个单元格表示,有利于加快解析速度。 XLS Area 使用excel注释标注表示,它需要被定义在excel 模板的第一个单元格(A1):
jx:area(lastCell = "D3")
jx:each 用于循环输出数据
jx:each(items="list" var="user" lastCell="D3")
each 可以有如下一些属性:
- items 上下文中集合的变量名;
- var 在遍历集合的时候每一条记录的变量名;
- area 该XLS Command的解析区域;
- direction 数据在excel中填充的方向,默认(DOWN)向下;
- select 其值为一个表达式,用来过滤数据。
运行生成结果
大数据导出
jxls底层还是使用POI导出excel,用POI导出excel,其中一个关键的问题导出excel占用内存过多,当导出大数据量的时候就会出现内存溢出。POI提供了SXSSFWorkbook来实现大数据量导出,上述的工具类中提供了一个变量是否采用SXSSFWorkbook导出,其中rowAccessWindowSize导出多少行数据进行提交,提升性能,而防止内存溢出的问题。
is = new FileInputStream(new File(template));
if (isSXSSFWorkbook)
{
book = WorkbookFactory.create(is);
transformer = PoiTransformer.createSxssfTransformer(book,
rowAccessWindowSize, true);
context = new PoiContext(map);
}
else
{
transformer = TransformerFactory.createTransformer(is, os);
context = new Context(map);
}
其他特性
1.jxls的重要的特性是可以通过定义模板动态的导出,例如上述导出的用户信息,如果我需要新增导出性别一列,只需模板新增一列即可,无需修改任何代码。
2.jxls可以直接将查询的结果集导出到excel中,从而减少了查询数据存放时占用的内存。