Java通过FreeMarker作为模板导出Excel
Posted Java_宇宁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java通过FreeMarker作为模板导出Excel相关的知识,希望对你有一定的参考价值。
导言
本文主要借助FreeMarker作为模板导出Excel
页面点击导出只需传入模板Id(templateId),即可实现导出组件
1. 定义Excel模板
2. 另存为xml文件
3. 项目下创建ftl文件
4. 格式化xml内容
5. 拷贝至ftl文件
6. 表设计(简化版)
CREATE TABLE `es_templates` (
`template_id` bigint(20) NOT NULL COMMENT '主键',
`template_name` varchar(255) DEFAULT NULL COMMENT '模板名',
`template_usage` varchar(255) DEFAULT NULL COMMENT '模板用途',
PRIMARY KEY (`template_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='导出模板(es_templates)';
模拟已插入一条模板数据
7. 导出条件实体类
简化版:假设条件就时间区间
import java.util.Date;
/**
* @program: choice-goods
* @description: 导出模板条件
* @packagename: com.meritlink.choice.goods.bean.vo
* @author: cxy
* @date: 2021-07-14 11:02:50
**/
public class TemplateExportVO {
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
public TemplateExportVO() {
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
}
8. 控制层
templateExport: 存在条件筛选导出,此处入参添加条件实体类(见第7步骤)
templateId: 假设前序工作已经添加模板,数据库(第6步骤)存在模板数据
此时页面触发导出,传入导出数据的条件以及选择的模板id
import com.meritlink.choice.goods.bean.vo.TemplateExportVO;
import com.meritlink.choice.goods.service.goods.ExportManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* @program: choice-goods
* @description: 导出控制层
* @packagename: com.meritlink.choice.goods.api.manager.goods
* @author: cxy
* @date: 2021-07-12 10:28:42
**/
@RestController
@RequestMapping("/admin/goods/export")
public class ExportManagerController {
@Autowired
private ExportManager exportManager;
@PostMapping("/{templateId}")
public void export(HttpServletRequest request,
HttpServletResponse response,
@RequestBody TemplateExportVO templateExport,
@PathVariable Long templateId) throws IOException {
this.exportManager.createFreeMarker(request, response, templateExport, templateId);
}
}
9. 接口
import com.meritlink.choice.goods.bean.vo.TemplateExportVO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* @program: choice-goods
* @description: 导出
* @packagename: com.meritlink.choice.goods.service.goods
* @author: cxy
* @date: 2021-07-12 10:41:14
**/
public interface ExportManager {
/**
* 导出
*
* @param request
* @param response
* @param templateExport
* @param templateId
* @return
*/
void createFreeMarker(HttpServletRequest request, HttpServletResponse response, TemplateExportVO templateExport, Long templateId) throws IOException;
}
10. 实现类
逻辑:
10-1: 根据templateId获取模板信息(此处省略查询数据库方法)
10-2: 获取templateName(后续会根据该参数获取ftl模板)以及templateUsage(后续会根据该参数获取渲染数据)
10-3: 获取数据
10-3-1: 通过工厂模式 + 策略模式获取数据
10-4: 创建excel
10-4-1: 根据模板名获取模板信息
10-4-2: 渲染数据
10-4-3: 将文件输出到response,返回给客户端
10-4-5: 关闭流以及删除临时文件
import com.meritlink.choice.framework.exception.ServiceException;
import com.meritlink.choice.goods.bean.dos.TemplatesDO;
import com.meritlink.choice.goods.bean.vo.TemplateExportVO;
import com.meritlink.choice.goods.common.code.GoodsErrorCode;
import com.meritlink.choice.goods.service.goods.ExportManager;
import com.meritlink.choice.goods.service.goods.TemplateManager;
import com.meritlink.choice.goods.service.strategy.templates.ITemplateStrategy;
import com.meritlink.choice.goods.service.strategy.templates.TemplateStrategyFactory;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.lang.StringUtils;
import org.apache.http.util.Asserts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @program: choice-goods
* @description: 导出
* @packagename: com.meritlink.choice.goods.service.goods.impl
* @author: cxy
* @date: 2021-07-12 10:41:37
**/
@Service
public class ExportManagerImpl implements ExportManager {
@Autowired
private TemplateManager templateManager;
@Autowired
private TemplateStrategyFactory templateStrategyFactory;
@Override
public void createFreeMarker(HttpServletRequest request, HttpServletResponse response, TemplateExportVO templateExport, Long templateId) {
try {
// 根据模板id获取模板
TemplatesDO template = getTemplateById(templateId);
String templateName = template.getTemplateName();
String templateUsage = template.getTemplateUsage();
if (StringUtils.isBlank(templateName)) {
throw new ServiceException(GoodsErrorCode.E301.code(), "模板名为空");
}
if (StringUtils.isBlank(templateUsage)) {
throw new ServiceException(GoodsErrorCode.E301.code(), "模板用途为空");
}
Object templateData = templateStrategyFactory.getStrategy(templateUsage).getTemplateData(templateExport);
Map<String, Object> map = new HashMap<>(16);
map.put("items", templateData);
// 调用创建excel
this.createExcel(map, templateName, response);
} catch (Exception e) {
e.printStackTrace();
}
}
private TemplatesDO getTemplateById(Long templateId) {
TemplatesDO template = templateManager.getTemplateNameById(templateId);
if (template == null) {
throw new ServiceException(GoodsErrorCode.E301.code(), "模板信息异常,不存在");
}
return template;
}
/**
* 创建excel
*
* @param dataMap
* @param templateName
* @param response
* @return
*/
private void createExcel(Map dataMap, String templateName, HttpServletResponse response) throws IOException {
FileInputStream inputStream = null;
ServletOutputStream outputStream = null;
File file = null;
try {
// Excel名
String excelName = templateName.split("[.]")[0] + System.currentTimeMillis() + ".xls";
response.reset();
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(excelName, "UTF8"));
response.setCharacterEncoding("utf-8");
response.setContentType("application/vnd.ms-excel;charset=utf-8");
// 创建配置对象
Configuration configuration = new Configuration(Configuration.getVersion());
configuration.setDefaultEncoding("utf-8");
// 设置模板路径
configuration.setClassForTemplateLoading(this.getClass(), "/templates");
// 构造输出流
Template template = configuration.getTemplate(templateName, "UTF-8");
file = new File(excelName);
FileWriter out = new FileWriter(excelName);
// 变量替换
template.process(dataMap, out);
// 将文件输出到response,返回给客户端
inputStream = new FileInputStream(file);
byte[] buffer = new byte[inputStream.available()];
inputStream.read(buffer);
inputStream.close();
outputStream = response.getOutputStream();
outputStream.write(buffer);
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null)
inputStream.close();
if (outputStream != null)
outputStream.close();
if (file != null)
// 删除临时文件
file.delete();
}
}
}
10-1. 根据templateId获取模板信息
此处省略查询数据库方法,可以根据自己项目获取数据的方式。
10-2. 获取templateName和templateUsage
templateUsage是枚举类的getCode值,后续添加模板对应添加枚举值
/**
* @program: choice-goods
* @description: 模板用途
* @packagename: com.meritlink.choice.goods.common.enums
* @author: cxy
* @date: 2021-07-14 10:25:46
**/
public enum TemplateUsageEnum {
GOODS("GOODS", "商品");
private String code;
private String desc;
TemplateUsageEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
10-3. 获取渲染数据
我这边通过工厂模式 + 策略模式获取模板的渲染数据,后续就可以不用再去修改该类以及工厂,只需要添加策略即可完成模板的添加
10-3-1. 通过工厂模式 + 策略模式获取数据
工厂
重写 setApplicationContext()
方法获取 ITemplateStrategy
接口所有的实现类的 value.getStrategyKey()
方法动态将所有的策略加入 templatesMap
通过 getStrategy()
方法获取策略
import com.meritlink.choice.framework.exception.ResourceNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @program: choice-goods
* @description: 导出模板工厂
* @packagename: com.meritlink.choice.goods.service.strategy.templates
* @author: cxy
* @date: 2021-07-14 11:12:30
**/
@Component
@Slf4j
public class TemplateStrategyFactory implements ApplicationContextAware {
private static final Map<String, ITemplateStrategy> templatesMap = new HashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, ITemplateStrategy> map = applicationContext.getBeansOfType(ITemplateStrategy.class);
map.forEach((key, value) -> templatesMap.put(value.getStrategyKey(), value));
}
public <T extends ITemplateStrategy> T getStrategy(String strategyKey) {
ITemplateStrategy strategy = templatesMap.get(ITemplateStrategy.EXPORT_TEMPLATE_STRATEGY_KEY_PREFIX + strategyKey);
if (strategy == null) {
log.error("TemplateStrategyFactory.getStrategy is error, strategyKey is {}", strategyKey);
throw new ResourceNotFoundException("TemplateStrategyFactory.getStrategy is error, strategyKey is " + strategyKey);
}
return (T) strategy;
}
}
策略
- 定义通用方法,即获取策略的key
/**
* @program: choice-goods
* @description: 策略基础接口
* @packagename: com.meritlink.choice.goods.service.strategy
* @author: cxy
* @date: 2021-07-14 10:48:27
**/
public interface BaseStrategy {
public final static String KEY_CONCAT = "_";
String getStrategyKey();
}
- 定义策略接口
import com.meritlink.choice.goods.bean.vo.TemplateExportVO;
import com.meritlink.choice.goods.service.strategy.BaseStrategy;
/**
* @program: choice-goods
* @description: 导出模板策略
* @packagename: com.meritlink.choice.goods.service.strategy.templates
* @author: cxy
* @date: 2021-07-14 10:53:21
**/
public interface ITemplateStrategy extends BaseStrategy {
public final static String EXPORT_TEMPLATE_STRATEGY_KEY_PREFIX = ITemplateStrategy.class.getSimpleName() + KEY_CONCAT;
/**
* 获取模板数据
*
* @param templateExport
* @return
*/
Object getTemplateData(TemplateExportVO templateExport);
}
- 添加策略的实现
import com.meritlink.choice.goods.bean.dos.GoodsDO;
import com.meritlink.choice.goods.bean.vo.TemplateExportVO;
import com.meritlink.choice.goods.common.enums.TemplateUsageEnum;
import com.meritlink.choice以上是关于Java通过FreeMarker作为模板导出Excel的主要内容,如果未能解决你的问题,请参考以下文章