spring boot使用AbstractXlsView导出excel
Posted 沧海一滴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring boot使用AbstractXlsView导出excel相关的知识,希望对你有一定的参考价值。
一、maven依赖jar包
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
二、导出view
public class ExportMemberVo {
private String name;
private Integer gender;
private String idCard;
private String bankNo;
private String bankName;
private String phone;
/**
* 性别处理
*/
public String getGender() {
return gender == 0 ? "男" : "女";
}
/****为了节省篇幅,省略setter/getter/constructor****/
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
三、导出Excel核心处理代码,继承自AbstractXlsView ,并实现buildExcelDocument
import export.entity.ExportMemberVo;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.springframework.web.servlet.view.document.AbstractXlsView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
/**
* @Author Kent.Wang
* @Date 2017/6/26
*/
public abstract class ExcelView extends AbstractXlsView {
@Override
protected void buildExcelDocument(Map<String, Object> map,
Workbook workbook,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
String excelName = map.get("name").toString() + ".xls";
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(excelName,"utf-8"));
response.setContentType("application/ms-excel; charset=UTF-8");
response.setCharacterEncoding("UTF-8");
@SuppressWarnings("unchecked")
List<ExportMemberVo> list = (List<ExportMemberVo>) map.get("members");
Sheet sheet = workbook.createSheet("User Detail");
sheet.setDefaultColumnWidth(30);
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("Arial");
style.setFillForegroundColor(HSSFColor.BLUE.index);
style.setFillPattern((short) 1);
font.setBold(true);
font.setColor(HSSFColor.WHITE.index);
style.setFont(font);
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("姓名");
header.getCell(0).setCellStyle(style);
header.createCell(1).setCellValue("性别");
header.getCell(1).setCellStyle(style);
header.createCell(2).setCellValue("手机号");
header.getCell(2).setCellStyle(style);
header.createCell(3).setCellValue("身份证号");
header.getCell(3).setCellStyle(style);
header.createCell(4).setCellValue("银行卡号");
header.getCell(4).setCellStyle(style);
int rowCount = 1;
for (ExportMemberVo user : list) {
Row userRow = sheet.createRow(rowCount++);
userRow.createCell(0).setCellValue(user.getName());
userRow.createCell(1).setCellValue(user.getGender());
userRow.createCell(2).setCellValue(user.getPhone());
userRow.createCell(3).setCellValue(user.getIdCard());
userRow.createCell(4).setCellValue(user.getBankNo());
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
四、controller代码
@RequestMapping(value = "", method = RequestMethod.GET)
public ModelAndView download() {
List<ExportMemberVo> list = new ArrayList<ExportMemberVo>();
for (int i = 0; i < 5; i++) {
ExportMemberVo exportMemberVo = new ExportMemberVo();
exportMemberVo.setName("Kent" + i);
@SuppressWarnings("unchecked")
int gender = ThreadLocalRandom.current().nextInt(0, 2);
exportMemberVo.setGender(gender);
exportMemberVo.setPhone("182xxxxxxxx");
exportMemberVo.setBankName("建设银行");
list.add(exportMemberVo);
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("members", list);
map.put("name", "魅力城市");
ExcelView excelView = new UserInfoExcelView();
return new ModelAndView(excelView, map);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
运行,访问download结果如下:
这是网络上一般的导出方法,没什么特别的,拿来即用,在使用的过程中也碰到一些问题,和疑问,下面谈谈我自己的理解。
五、火狐浏览器导出excel中文乱码问题
这个问题是因浏览器的不同所造成的,那只要对response.setHeader做些处理就可以了。我只测试了chrome和firefox,其他浏览器或许还有些差异,在此不一一枚举。
String Agent = request.getHeader("User-Agent");
if (null != Agent) {
Agent = Agent.toLowerCase();
if (Agent.indexOf("firefox") != -1) {
response.setHeader("content-disposition", String.format("attachment;filename*=utf-8‘zh_cn‘%s", URLEncoder.encode(excelName, "utf-8")));
} else {
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(excelName, "utf-8"));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
六、buildExcelDocument是怎么被调用的
我们看到继承自AbstractXlsView 的buildExcelDocument方法是protected的,只能被同一包下面和子类调用,理论上controller不会去继承ExcelView ,也不会在同一包下,那我们如何去调用他,在springMVC和spring boot下,我们需要用到ModelAndView。
我通过两幅图来列举下buildExcelDocument调用的过程:
download方法执行return之后会大致执行如下过程,我们可以看到buildExcelDocument是如何被调用的,具体过程有兴趣可以自己debug。
其中比较重要的一个环节是view.render,这里用到了Java的多态特性,AbstractView是继承自View的,ExcelView自然也继承View,所以获取到的ModelAndView中的view执行view.render实际上会去调用AbstractView的render方法,然后AbstractView中有个抽象方法renderMergeOutputModel,供子类实现不同的输出模型,输出Excel文件就是其中的一个子类,还有输出PDF文件也是同理实现该方法。
具体handle方法返回ModelAndView的过程如下:
七、包装一下Excel输出模版
我们可能需要输出许多Excel,有成员信息,商品信息,规格信息等等。所以将所有设置Excel的代码全放在ExcelView类中有点不合时宜,很显然至少列名和值的绑定都写死了,难以扩展。
仿照AbstractView中用到的模板方法模式,我们也可以将具体设置Sheet提取出来,写个抽象方法,由子类去实现具体设置Sheet.
修改过的ExcelView如下:
public CellStyle cellStyle;
/**
* 设置样式
*
* @param workbook
*/
protected abstract void setStyle(Workbook workbook);
/**
* 设置Row,由子类实现
*
* @param sheet
* @param map
*/
protected abstract void setRow(Sheet sheet, Map<String, Object> map);
@Override
protected void buildExcelDocument(Map<String, Object> map,
Workbook workbook,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
String excelName = map.get("name").toString() + ".xls";
String Agent = request.getHeader("User-Agent");
if (null != Agent) {
Agent = Agent.toLowerCase();
if (Agent.indexOf("firefox") != -1) {
response.setHeader("content-disposition", String.format("attachment;filename*=utf-8‘zh_cn‘%s", URLEncoder.encode(excelName, "utf-8")));
} else {
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(excelName, "utf-8"));
}
}
response.setContentType("application/ms-excel; charset=UTF-8");
Sheet sheet = workbook.createSheet("User Detail");
sheet.setDefaultColumnWidth(30);
this.setStyle(workbook);
setRow(sheet, map);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
我们写个导出成员信息UserInfoExcelView ,所有Sheet设置都在这里完成:
public class UserInfoExcelView extends ExcelView {
@Override
public void setRow(Sheet sheet, Map<String, Object> map) {
// create header row
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("姓名");
header.getCell(0).setCellStyle(super.cellStyle);
header.createCell(1).setCellValue("性别");
header.getCell(1).setCellStyle(super.cellStyle);
header.createCell(2).setCellValue("手机号");
header.getCell(2).setCellStyle(super.cellStyle);
header.createCell(3).setCellValue("身份证号");
header.getCell(3).setCellStyle(super.cellStyle);
header.createCell(4).setCellValue("银行卡号");
header.getCell(4).setCellStyle(super.cellStyle);
@SuppressWarnings("unchecked")
List<ExportMemberVo> list = (List<ExportMemberVo>) map.get("members");
int rowCount = 1;
for (ExportMemberVo user : list) {
Row userRow = sheet.createRow(rowCount++);
userRow.createCell(0).setCellValue(user.getName());
userRow.createCell(1).setCellValue(user.getGender());
userRow.createCell(2).setCellValue(user.getPhone());
userRow.createCell(3).setCellValue(user.getIdCard());
userRow.createCell(4).setCellValue(user.getBankNo());
}
}
@Override
protected void setStyle(Workbook workbook) {
DefaultCellStyle defaultCellStyle = new DefaultCellStyleImpl();
super.cellStyle = defaultCellStyle.setCellStyle(workbook);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
如果还需要导出其他excel,同样继承下ExcelView并实现setRow和setStyle就可以了。
由于样式可能会设置通用的,但又有扩展的可能性,所以可以实现一个默认样式接口。
DefaultCellStyle.class
public interface DefaultCellStyle {
CellStyle setCellStyle(Workbook workbook);
}
- 1
- 2
- 3
DefaultCellStyleImpl.class
public class DefaultCellStyleImpl implements DefaultCellStyle {
@Override
public CellStyle setCellStyle(Workbook workbook) {
// create style for header cells
CellStyle cellStyle = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("Arial");
cellStyle.setFillForegroundColor(HSSFColor.BLUE.index);
cellStyle.setFillPattern((short) 1);
font.setBold(true);
font.setColor(HSSFColor.WHITE.index);
cellStyle.setFont(font);
return cellStyle;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
https://blog.csdn.net/wang124454731/article/details/73850645
以上是关于spring boot使用AbstractXlsView导出excel的主要内容,如果未能解决你的问题,请参考以下文章
spring boot8.spring boot的日志框架使用
(转)Spring Boot 2 :使用 Docker 部署 Spring Boot