您的位置:首页 > 编程语言 > Java开发

spring boot使用AbstractXlsView导出excel

2017-06-28 17:15 489 查看

一、maven依赖jar包

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>


二、导出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****/
}


三、导出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());
}
}
}


四、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);
}


运行,访问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"));
}
}


六、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);
}


 我们写个导出成员信息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);
}

}


如果还需要导出其他excel,同样继承下ExcelView并实现setRow和setStyle就可以了。

由于样式可能会设置通用的,但又有扩展的可能性,所以可以实现一个默认样式接口。

DefaultCellStyle.class

public interface DefaultCellStyle {
CellStyle setCellStyle(Workbook workbook);
}


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;
}
}


主要代码可在这下载:http://download.csdn.net/detail/wang124454731/9883223

欢迎关注我的个人公众号:逍遥的心。主推程序员写的生活类文章,有兴趣的朋友可以共同探讨下:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring excel 中文乱码