使用Apache poi来编写导出excel的工具类
在JavaWeb开发的需求中,我们会经常看到导出excel的功能需求,然后java并没有提供操作office文档的功能,这个时候我们就需要使用额外的组件来帮助我们完成这项功能了。
很高兴Apache基金会为我们提供了poi组件,让我们能够在它的帮助下操作office文档,下面就让我们来使用它来制作导出excel的工具类。
一.前期说明
1.我们要理解一个excel文件的组成。首先要有一个workbook(工作簿,即excel文件本身),然后workbook中有几个sheet(工作表),sheet中是由row(行)和column(列)构成的,行和列确定一个唯一的cell(单元格)。理清了这几个组成部分,我们再来理解工具类的就比较容易了。
2.office文件分为97-2003版本和2007版本,这两个是截然不同的文件格式,97-2003的excel的文件后缀为.xsl,而2007的excel的文件后缀为.xslx。我们在这里暂时只讨论97-2003版本的操作,至于2007版本的操作原理相同,只是所使用的类不同。
3.操作excel 97-2003的文件,只需要引入poi-version-yyyymmdd.jar(例如poi-3.13-20150929.jar)即可,其余jar包的用途,详情请百度“poi各jar包的作用”。
二.代码分析
工具类代码
package com.billionsfinance.bqap.util;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import com.billionsfinance.common.Exception.BizException; public class ExportExcel<T> {
/**
* 利用了JAVA的反射,将javabean的集合以一定的格式输出到指定IO设备上
* @param sheetName
* 工作表标题
* @param headerMap
* 表格列属性及属性名
* @param dataset
* 需要显示的数据集合
* @param out
* 与输出设备相关的流对象,可以将Excel文档导出到本地或者网络中
* @throws IOException
*/
public void exportExcel(String sheetName, Map<String, String> headerMap, Collection<T> dataset, OutputStream out) throws IOException{
//声明一个工作簿
HSSFWorkbook workbook = new HSSFWorkbook();
//生成一个工作表
HSSFSheet sheet = workbook.createSheet(sheetName);
//设置表格默认列宽
//有些资料在设置宽度的时候用的是short(20),但是在实际使用的过程中,short(20)已不被推荐使用,所以直接使用int(20)
sheet.setDefaultColumnWidth(20);
//获取属性Map的keySet
Set<String> keySet = headerMap.keySet();
//获取属性的数组
String[] fields = keySet.toArray(new String[0]);
//对keySet进行遍历,获得属性对应的属性名称来构成标题行
Iterator<String> key = keySet.iterator();
//第一行
HSSFRow row = sheet.createRow(0);
for(int i = 0; key.hasNext(); i++){
//产生单元格
HSSFCell cell = row.createCell(i);
//产生标题行
HSSFRichTextString title = new HSSFRichTextString(headerMap.get(key.next()));
//为单元格赋值
cell.setCellValue(title);
}
/*
* 遍历数据集合,产生数据行
* 利用反射,通过属性数组来拼接getXXX的方法去获得属性的值
*/
//属性数组的长度
int size = fields.length;
//属性的get方法的数组
String[] methodNames = new String[size];
for(int i = 0; i < size; i++){
//拼凑属性的getter方法
methodNames[i] = "get"
+ fields[i].substring(0, 1).toUpperCase()
+ fields[i].substring(1);
}
Iterator<T> ite = dataset.iterator();
for(int index = 1; ite.hasNext(); index++){
row = sheet.createRow(index);
T t = (T)ite.next();
for(int i = 0; i < size; i++){
HSSFCell cell = row.createCell(i);
try{
Class<?> cla = t.getClass();
Method getMethod = cla.getMethod(methodNames[i], new Class[]{});
Object value = getMethod.invoke(t, new Object[]{});
String textValue = null;
//如果属性的类型是String,则直接输出字符串
if(value instanceof String){
textValue = value.toString();
}
//如果属性的类型是Date,则需要格式转换
if(value instanceof Date){
Date date = (Date)value;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
textValue = sdf.format(date);
}
//如果是字符串,则利用正则表达式判断textValue是否全部由数字组成
if(textValue != null){
Pattern p = Pattern.compile("^//d+(//.//d+)?$");
Matcher matcher = p.matcher(textValue);
if(matcher.matches()){
//是数字则当做double处理
cell.setCellValue(Double.parseDouble(textValue));
}else{
HSSFRichTextString richString = new HSSFRichTextString(textValue);
cell.setCellValue(richString);
}
}
}catch(BizException ex){
LogUtil.ERROR.error(ex, ex);
}catch(Exception e){
e.printStackTrace();
}
}
}
//将workbook输出到输出设备中
workbook.write(out);
//关闭资源
out.flush();
workbook.close();
out.close();
}
}
代码说明:
1.sheetName,即工作表的名称,这里只有一张工作表,所以一次导出的数据不能多于6万条。注意,一次导出太多的数据会消耗服务器内存,有可能导致服务器宕机,所以导出的时候最好对一次性导出数据量做限定。
2.headerMap,此处应传入一个linkedHashMap<String,String>对象。linkedHashMap既指定了属性和属性名称(name:名称)的对应关系,又保证了属性与属性之间的顺序,这样我们就能很好的控制在excel表格中属性的顺序。
3.dataset,这里使用的泛型,不过我们在实际开发中一般都是使用List集合来传递数据。
4.out,输出设备,这里既可以是屏幕设备,也可以是文件系统,当然也可以是response响应了。
代码分析:
1.在代码中,我尽量避免使用java的反射,因为反射是比较消耗性能的操作,只在获得getter方法的时候使用了反射。属性的获取是通过headerMap遍历,这么做一是节省了性能的消耗,二是可以指定需要导出的属性。例如我们在使用实体类的时候经常需要序列化,那么实体类中肯定有serialVersionUID属性,然而这个属性我们是不需要导出的。我们只需要在headerMap中添加需要导出的属性即可做到剔除不需要的属性。
三.使用
1.bean
package com.billionsfinance.bqap.bean;
public class SmsFailHistoryBean implements java.io.Serializable{
private static final long serialVersionUID = 5449014577923870013L;
private String id; //流水号
private String sender; //发送人
private String appTypeName; //发送类型名称
private String phone; //手机号码
private String message; //短信内容
private String supplier; //供应商
private String status; //状态
private String submitTime; //提交时间
private String createTime; //创建时间
private String appType; //发送类型
/* getter and setter */
}
这个bean就是我们在项目开发中经常使用的实体类,看,其中就有serialVersionUID属性,可是我们并不需要导出它。
2.controller
package com.billionsfinance.bqap.controller; import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import com.billionsfinance.bqap.bean.SmsFailHistoryBean;
import com.billionsfinance.bqap.bean.SmsSendHistoryBean;
import com.billionsfinance.bqap.entity.Config;
import com.billionsfinance.bqap.server.IConfigServer;
import com.billionsfinance.bqap.server.ISmsSendHistoryServer;
import com.billionsfinance.bqap.util.Constant;
import com.billionsfinance.bqap.util.ExportExcel;
import com.billionsfinance.bqap.util.GroupNoManager;
import com.billionsfinance.bqap.util.StringUtils;
import com.billionsfinance.common.framework.RemoteCall; /**
* 导出excel表格,暂时只能用来导出发送历史和失败历史
* @author XIAYU
*
*/
@Controller
public class ExportExcelController {
/*
* 导出发送历史或发送失败历史
*/
@RequestMapping("/exportExcel")
public void exportExcel(HttpServletRequest request, HttpServletResponse response) throws Exception{
ISmsSendHistoryServer smsSendHistoryServer = RemoteCall.getRemoteObject(ISmsSendHistoryServer.class, GroupNoManager.getGroupNo(request));
//获取查询条件
Map<String, String> params = this.setParmas(request);
//文件名称
String filename = null;
response.reset();
//设置响应头
response.setContentType("application/msexcel");
OutputStream out = response.getOutputStream();
Map<String, String> headerMap = new LinkedHashMap<String, String>();
String page = request.getParameter("page");
//如果是smsSendHistoryList.jsp页面发过来的则导出发送历史
if("smsSendHistory".equals(page)){
filename = "发送历史总记录.xls";
response.setHeader("Content-disposition", "attachment;filename=" + encodeChineseDownloadFileName(request, filename));
ExportExcel<SmsSendHistoryBean> ex = new ExportExcel<SmsSendHistoryBean>();
headerMap.clear();
headerMap.put("id", "流水号");
headerMap.put("sender", "发送人");
headerMap.put("appTypeName", "发送类型");
headerMap.put("message", "短信内容");
headerMap.put("supplier", "供应商");
headerMap.put("success", "成功条数");
headerMap.put("successPhone", "成功手机号码");
headerMap.put("failure", "失败条数");
headerMap.put("failurePhone", "失败手机号码");
headerMap.put("count", "总条数");
headerMap.put("sendTime", "定时发送时间");
headerMap.put("createTime", "创建时间");
//获取需要导出的数据集合
List<SmsSendHistoryBean> dataset = smsSendHistoryServer.listSmsSendHistory(params);
ex.exportExcel("发送历史", headerMap, dataset, out);
}
//如果是smsFailHistoryList.jsp页面发过来的则导出失败历史
if("smsFailHistory".equals(page)){
filename = "短信发送失败记录.xls";
response.setHeader("Content-disposition", "attachment;filename=" + encodeChineseDownloadFileName(request, filename));
ExportExcel<SmsFailHistoryBean> ex = new ExportExcel<SmsFailHistoryBean>();
headerMap.clear();
headerMap.put("id", "流水号");
headerMap.put("sender", "发送人");
headerMap.put("appTypeName", "发送类型");
headerMap.put("phone", "手机号码");
headerMap.put("message", "短信内容");
headerMap.put("supplier", "供应商");
headerMap.put("status", "状态");
headerMap.put("submitTime", "提交时间");
headerMap.put("createTime", "创建时间");
//获取需要导出的数据集合
List<SmsFailHistoryBean> dataset = smsSendHistoryServer.listSmsFailHistory(params);
ex.exportExcel("失败历史", headerMap, dataset, out);
}
if(out != null){
out.flush();
out.close();
}
} /**
* 根据配置代码获取获取枚举参数信息
* @param request
* @param configCode
* @return
*/
private List<Config> getConfigList(HttpServletRequest request,String configCode){
IConfigServer configserver =RemoteCall.getRemoteObject(IConfigServer.class, GroupNoManager.getGroupNo(request));
Config config = new Config();
config.setConfigCode(configCode);
List<Config> list = configserver.listConfig(config);
return list;
} /**
* 存储参数,保证页面刷新后参数还在查询栏中
* 返回参数,用于查询条件
* @param request
* @return
*/
private Map<String,String> setParmas(HttpServletRequest request){
List<Config> appList = this.getConfigList(request, Constant.APP_CODE);
request.setAttribute("appList", appList);
List<Config> supplierList = this.getConfigList(request, Constant.SUPPLIER_CODE);
request.setAttribute("supplierList", supplierList);
Map<String,String> params = new HashMap<String,String>();
String appCode = request.getParameter("appCode");
String sendType = request.getParameter("sendType");
String supplier = request.getParameter("supplier");
String startTime = request.getParameter("startTime");
String endTime = request.getParameter("endTime");
params.put("appCode", appCode);
params.put("sendType", sendType);
params.put("supplier", supplier);
params.put("startTime", startTime);
params.put("endTime", endTime);
request.setAttribute("appCode", appCode);
request.setAttribute("sendType", sendType);
request.setAttribute("supplier", supplier);
request.setAttribute("startTime", startTime);
request.setAttribute("endTime", endTime);
return params;
}
/**
* 解决浏览器对文件中文名的编码问题
* 不同的浏览器对文件中文名的处理方法不同
* @param request
* @param pFileName
* @return
*/
private String encodeChineseDownloadFileName(HttpServletRequest request, String pFileName) throws UnsupportedEncodingException{
String filename = null;
//获取请求头中的浏览器标识
String agent = request.getHeader("USER-AGENT");
if(agent != null){
if(agent.indexOf("Firefox") != -1){
//Firefox
filename = "=?UTF-8?B?" +
(new String(Base64.encodeBase64(pFileName.getBytes("UTF-8")))) + "?=";
}else if(agent.indexOf("Chrome") != -1){
//Chrome
filename = new String(pFileName.getBytes(), "ISO8859-1");
}else{
//IE7+
filename = URLEncoder.encode(pFileName, "UTF-8");
//替换空格
filename = StringUtils.replace(filename, "+", "%20");
}
}else{
filename = pFileName;
}
return filename;
}
}
这个Controller是springMVC下的Controller,使用注解。hessian调用不必太在意,主要是注意调用工具类时应传入的参数。
3.html
<a href="${ctx}/exportExcel.action?page=smsFailHistory¶ms=...">导出</a>
只需要一个a标签或者一个button就能触发导出事件呢,这里要注意一个问题,在导出操作的时候不要使用ajax方式,否则返回的文件数据会直接打印到浏览器的后台中,而不是输出到文件系统。所以就按照平常的方式去提交请求就好。
四.总结
这只是poi操作excel文件的一个简单示例,它还有其他很多的功能,比如改变excel数据的样式,编写excel公式等等,,,这些功能需要熟读api,待以后有时间,再来写博。
无他,唯手熟尔~
使用Apache poi来编写导出excel的工具类的更多相关文章
- apache poi根据模板导出excel
需要预先新建编辑好一个excel文件,设置好样式. 编辑好输出的数据,根据excel坐标一一对应. 支持列表数据输出,列表中列合并. 代码如下: package com.icourt.util; im ...
- 使用POI插件,提取导出excel的工具类
在网站的不同的模块都需要使用到导入导出excel的功能,我们就需要写一个通用的工具类ExcelUtil. 我的思路:首先,导入和导出的Excel的文件格式固定:主标题,二级标题,数据行(姑且就这么叫) ...
- POI导入导出excel(附工具类)
关于POI导出excel的功能我在前面的文章已经写过了,POI导出excel的三种方式 , 导出表格数据到excel并下载(HSSFWorkbook版) ,本篇文章主要是将导入导出功能进一步地封装,在 ...
- 导出excel文件工具类
package com.rrz.common.utils.excel; import java.io.IOException;import java.io.OutputStream;import ja ...
- java web 导出Excel 的工具类公用实现
平时我们在开发后端管理的系统时,经常会出现导出Excel的功能.这个功能很简单,但是呢,我一般就会使用公用的工具类,在一个Util中,暴露公用导出的接口,只需要传入需要导出的数据(一般使用list集合 ...
- 导出excel(利用工具类导出excel)
/** * 添加导出功能 * @param creditPageResult * @param request * @param response */ @RequestMapping(value = ...
- Java使用POI实现数据导出excel报表
Java使用POI实现数据导出excel报表 在上篇文章中,我们简单介绍了java读取word,excel和pdf文档内容 ,但在实际开发中,我们用到最多的是把数据库中数据导出excel报表形式.不仅 ...
- excel读取 工具类
package cn.yongche.utils; import java.io.File; import java.io.FileInputStream; import java.io.IOExce ...
- Java 使用Apache POI读取和写入Excel表格
1,引入所用的包 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxm ...
随机推荐
- Python设计模式: 最佳的"策略"模式实践代码
Python设计模式: 最佳的"策略"模式实践代码 今天抽空看了下流畅的python,发现里面介绍了不少python自带的库的使用实例,用起来非常的优雅. 平时用Python来写爬 ...
- template.js模板工具案例
案例一 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset=&qu ...
- python,ctf笔记随笔
一.在centos虚拟机中安装pyhton3环境: 安装pip3:yum install python36-pip 将pip升级到最新版本:pip3 install --upgrade pip 运行p ...
- Windows协议 Kerberos篇
认证流程 角色 功能 Domain Controller 也就是域控 Key Distribution Center 秘钥分发中心,简称KDC,默认安装在域控里,包括AS.AD和TGS. Accoun ...
- java数据类型和类型得转换
java数据类型 强类型语言 Java是一种强类型得语言,严格要求变量要符合规定,所有变量都必须先定义再使用 java得数据类型分为两大类 值得注意得是String并不是一个数据类型,它是一个类 ...
- Java-Dubbo学习及整合SpringBoot
Dubbo架构 Dubbo是Java的RPC框架,具有三大核心功能:面向接口的远程方法调用,智能容错和负载均衡,以及服务的自动注册和发现 Dubbo架构图: 节点角色说明: 节点 说明 Provide ...
- dubbo学习实践(1)之管理控制台Dubbo-admin部署
1.Docker拉取现有镜像,构建Dubbo-admin 拉取镜像,这里使用chenchuxin/dubbo-admin docker pull chenchuxin/dubbo-admin 注册中心 ...
- 说说XXE漏洞那些事
想不起来写点啥了,又是摸鱼的一天,看了一些红队大佬们整理的资料,非常精彩,于是一个咸鱼翻身先选了一些简单的小点来写一写个人的感想(后续会继续更新其他内容) 不能说写的是技术分享,因为师傅们的文章珠玉在 ...
- HTTP头参数详解及其中的危险
一.重要的头参数 user_agent 发出请求的用户信息 X-Forwarded-For 表示 HTTP 请求端真实 IP(格式:X-Forwarded-For: client, proxy1, p ...
- S3C2440—11.und异常
文章目录 1 未定义指令 2 中断向量表 3 设置一个未定义指令 4 调用C函数 5 UND异常处理程序 6 汇编源码 7 注意点 lr与pc 保存现场 中断向量表的跳转 程序执行顺序 问题 1 未定 ...