在pom.xml中加入POI的依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.11-beta1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.11-beta1</version>
</dependency>
自定义注解,用于定义excel单元格的相关信息,用在需要导出的类上。
大家可以根据自己的实际需求来定义更多的内容。
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelResources {
int order() default 9999;//定义字段在excel的单元格列坐标位置
String title() default "";//定义列坐标对应的标题
int cloumn() default 100;//定义列宽
String pattern() default "";//定义日期显示格式
}
举例说明@ExcelResources 的应用场景,我们创建一个demoModel,包含姓名、年龄、性别、日期。
后边的excel导出例子也采用这个实体类来举例。
@Data
public class ExcelDemoModel {
@ExcelResources(order=0,title = "姓名",cloumn = 10)
private String name;
@ExcelResources(order=1,title = "年龄",cloumn = 10)
private Integer age;
@ExcelResources(order=2,title = "创建时间",cloumn = 24,pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@ExcelResources(order=3,title = "性别",cloumn = 10)
private SexType sex;//枚举
}
用于存放导出的excel对应标题和列宽
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TitleAndCloumn {
private String title;//标题
private int cloumn;//列宽
}
@Service
public class ExcelService {
private static float title_row_height=30;//标题行高
private static float data_row_height=25;//数据行高
public void exportExcel(HttpServletRequest request, HttpServletResponse response, String fileName ,List<?> excelDatas,Class<?> clz ) {
try {
HSSFWorkbook resultWb=new HSSFWorkbook();
HSSFSheet sheet=resultWb.createSheet();//创建sheet
//根据类类型信息获取导出的excel对应的标题和列宽 key-列号,value-标题和列宽
HashMap<Integer, TitleAndCloumn> orderTitleAndCloumnMap=getTitleAndCloumnMap(clz);
//设置列宽
orderTitleAndCloumnMap.forEach((k,v) -> {
sheet.setColumnWidth(k, v.getCloumn()*256);
});
HSSFRow row0=sheet.createRow(0);
//设置标题行高
row0.setHeightInPoints(title_row_height);
//创建标题单元格格式
HSSFCellStyle titleCellStyle=getCellStyle(resultWb,11,true,HSSFColor.BLACK.index);
//填充标题行内容
orderTitleAndCloumnMap.forEach((k,v) -> {
HSSFCell row0Cell=row0.createCell(k);
row0Cell.setCellValue(v.getTitle());
row0Cell.setCellStyle(titleCellStyle);
});
//创建正文单元格格式
HSSFCellStyle dataStyle = getCellStyle(resultWb,11,false,HSSFColor.BLACK.index);
//将正文转换为excel数据
int rowNum=1;
for(Object data:excelDatas){
HSSFRow row=sheet.createRow(rowNum++);
row.setHeightInPoints(data_row_height);
//获取对象值 key-列号 value-String值
HashMap<Integer,String> orderValueMap=getValueMap(data);
orderValueMap.forEach((k,v) ->{
HSSFCell cell=row.createCell(k);
cell.setCellValue(v);
cell.setCellStyle(dataStyle);
}
);
}
String downFileName=fileName+".xls";
response.setContentType("application/vnd.ms-excel; charset=UTF-8");// application/x-download
response.setHeader("Content-Disposition", "attachment; "
+encodeFileName(request, downFileName));
OutputStream outputStream = response.getOutputStream();
resultWb.write(outputStream);
outputStream.flush();
outputStream.close();
resultWb.close();
}catch (Exception e1) {
e1.printStackTrace();
}
}
}
/**
* 获取类的属性对应单元格标题和列宽
* @param
* @return
*/
private static HashMap<Integer, TitleAndCloumn> getTitleAndCloumnMap(Class<?> clz) {
HashMap<Integer, TitleAndCloumn> orderTitleAndCloumnMap=new HashMap<>();
Field[] fs = clz.getDeclaredFields();
for(Field f:fs) {
f.setAccessible(true);
if(f.isAnnotationPresent(ExcelResources.class)) {
Integer order=f.getAnnotation(ExcelResources.class).order();
String title=f.getAnnotation(ExcelResources.class).title();
int cloumn=f.getAnnotation(ExcelResources.class).cloumn();
TitleAndCloumn titleAndCloumn=new TitleAndCloumn(title,cloumn);
orderTitleAndCloumnMap.put(order,titleAndCloumn);
}
}
return orderTitleAndCloumnMap;
}
通过传入参数定义简单地CellStyle
public HSSFCellStyle getCellStyle(HSSFWorkbook workbook,int fontSize,boolean isBoleaWeight,short color){
HSSFCellStyle style = workbook.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);//水平居中
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);//垂直居中
style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style.setBorderRight(HSSFCellStyle.BORDER_THIN);
style.setBorderTop(HSSFCellStyle.BORDER_THIN);
HSSFFont font = workbook.createFont();
font.setFontHeightInPoints((short) fontSize);//字号
font.setColor(color);//颜色
font.setFontName("宋体");//字体
if(isBoleaWeight){
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //字体加粗
}
style.setWrapText(true);
style.setFont(font);
return style;
}
我这里只涉及到基本数据类型和Date以及枚举的值获取和转换,小伙伴可以根据自己的实际情况进行修改。
/**
* 获取对象的属性对应单元格坐标和值的键值对
* @param obj
* @return
*/
private static HashMap<Integer, String> getValueMap(Object obj) throws IllegalAccessException {
HashMap<Integer, String> result=new HashMap<>();
Class<?> clz=obj.getClass();
Field[] fs = clz.getDeclaredFields();
for(Field f:fs) {
f.setAccessible(true);
if(f.isAnnotationPresent(ExcelResources.class)) {
Integer order=f.getAnnotation(ExcelResources.class).order();
String value="";
Object valueObj=f.get(obj);
if(valueObj!=null) {
//日期格式进行特殊处理
if(f.getType()==Date.class){
String pattern=f.getAnnotation(ExcelResources.class).pattern();
if(StringUtils.isEmpty(pattern)){
pattern="yyyy-MM-dd HH:mm:ss";
}
SimpleDateFormat sdf=new SimpleDateFormat(pattern);
value=sdf.format(valueObj);
}else{
value=valueObj.toString();//其他格式调用toString方法,这里枚举就需要定义自己的toString方法
}
}
result.put(order, value);
}
}
return result;
}
如果有用到枚举存储在数据库的小伙伴,可以自定义枚举的toString方法来实现excel导出时候相应的内容
public enum SexType {
male("男"),
female("女"),
;
private String typeName;
SexType(String typeName) {
this.typeName = typeName;
}
@Override
public String toString() {
return typeName;
}
}
/**
* 根据不同的浏览器生成不同类型中文文件名编码
*
* @param request
* @param fileName
* @return
* @throws UnsupportedEncodingException
*/
public static String encodeFileName(HttpServletRequest request, String fileName)
throws UnsupportedEncodingException
{
String new_filename = URLEncoder.encode(fileName, "UTF8").replaceAll("\\+", "%20");
String agent = request.getHeader("USER-AGENT").toLowerCase();
if (null != agent && -1 != agent.indexOf("msie"))
{
/**
* IE浏览器,只能采用URLEncoder编码
*/
return "filename=\"" + new_filename +"\"";
}else if (null != agent && -1 != agent.indexOf("applewebkit")){
/**
* Chrome浏览器,只能采用ISO编码的中文输出
*/
return "filename=\"" + new String(fileName.getBytes("UTF-8"),"ISO8859-1") +"\"";
} else if (null != agent && -1 != agent.indexOf("opera")){
/**
* Opera浏览器只可以使用filename*的中文输出
* RFC2231规定的标准
*/
return "filename*=" + new_filename ;
}else if (null != agent && -1 != agent.indexOf("safari")){
/**
* Safani浏览器,只能采用iso编码的中文输出
*/
return "filename=\"" + new String(fileName.getBytes("UTF-8"),"ISO8859-1") +"\"";
}else if (null != agent && -1 != agent.indexOf("firefox"))
{
/**
* Firfox浏览器,可以使用filename*的中文输出
* RFC2231规定的标准
*/
return "filename*=" + new_filename ;
} else
{
return "filename=\"" + new_filename +"\"";
}
}
public void exportExcelDemo(HttpServletRequest request, HttpServletResponse response) {
//一系列查询处理
List<ExcelDemoModel> demoList=new ArrayList<>();
excelService.exportExcel(request,response,"人员信息demo",demoList,ExcelDemoModel.class);
}