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

JAVA工具类(17)--Java导入导出Excel工具类ExcelUtil

2016-07-25 12:11 519 查看
实战

导出就是将List转化为Excel(listToExcel)

导入就是将Excel转化为List(excelToList)

导入导出中会出现各种各样的问题,比如:数据源为空、有重复行等,我自定义了一个ExcelException异常类,用来处理这些问题。

ExcelException类

package common.tool.excel;

public class ExcelException extends Exception {

public ExcelException() {
// TODO Auto-generated constructor stub
}

public ExcelException(String message) {
super(message);
// TODO Auto-generated constructor stub
}

public ExcelException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}

public ExcelException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}

}


下面就是该文的主角ExcelUtil登场了,作为一个工具类,其内的所有方法都是静态的,方便使用。

ExcelUitl类

/**
* @author     : WH
* @group      : tgb8
* @Date       : 2014-1-2 下午9:13:21
* @Comments   : 导入导出Excel工具类
* @Version    : 1.0.0
*/

public class ExcelUtil  {

/**
* @MethodName  : listToExcel
* @Description : 导出Excel(可以导出到本地文件系统,也可以导出到浏览器,可自定义工作表大小)
* @param list      数据源
* @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
* 如果需要的是引用对象的属性,则英文属性使用类似于EL表达式的格式
* 如:list中存放的都是student,student中又有college属性,而我们需要学院名称,则可以这样写
* fieldMap.put("college.collegeName","学院名称")
* @param sheetName 工作表的名称
* @param sheetSize 每个工作表中记录的最大个数
* @param out       导出流
* @throws ExcelException
*/
public static <T>  void   listToExcel (
List<T> list ,
LinkedHashMap<String,String> fieldMap,
String sheetName,
int sheetSize,
OutputStream out
) throws ExcelException{

if(list.size()==0 || list==null){
throw new ExcelException("数据源中没有任何数据");
}

if(sheetSize>65535 || sheetSize<1){
sheetSize=65535;
}

//创建工作簿并发送到OutputStream指定的地方
WritableWorkbook wwb;
try {
wwb = Workbook.createWorkbook(out);

//因为2003的Excel一个工作表最多可以有65536条记录,除去列头剩下65535条
//所以如果记录太多,需要放到多个工作表中,其实就是个分页的过程
//1.计算一共有多少个工作表
double sheetNum=Math.ceil(list.size()/new Integer(sheetSize).doubleValue());

//2.创建相应的工作表,并向其中填充数据
for(int i=0; i<sheetNum; i++){
//如果只有一个工作表的情况
if(1==sheetNum){
WritableSheet sheet=wwb.createSheet(sheetName, i);
fillSheet(sheet, list, fieldMap, 0, list.size()-1);

//有多个工作表的情况
}else{
WritableSheet sheet=wwb.createSheet(sheetName+(i+1), i);

//获取开始索引和结束索引
int firstIndex=i*sheetSize;
int lastIndex=(i+1)*sheetSize-1>list.size()-1 ? list.size()-1 : (i+1)*sheetSize-1;
//填充工作表
fillSheet(sheet, list, fieldMap, firstIndex, lastIndex);
}
}

wwb.write();
wwb.close();

}catch (Exception e) {
e.printStackTrace();
//如果是ExcelException,则直接抛出
if(e instanceof ExcelException){
throw (ExcelException)e;

//否则将其它异常包装成ExcelException再抛出
}else{
throw new ExcelException("导出Excel失败");
}
}

}

/**
* @MethodName  : listToExcel
* @Description : 导出Excel(可以导出到本地文件系统,也可以导出到浏览器,工作表大小为2003支持的最大值)
* @param list      数据源
* @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
* @param out       导出流
* @throws ExcelException
*/
public static  <T>  void   listToExcel (
List<T> list ,
LinkedHashMap<String,String> fieldMap,
String sheetName,
OutputStream out
) throws ExcelException{

listToExcel(list, fieldMap, sheetName, 65535, out);

}

/**
* @MethodName  : listToExcel
* @Description : 导出Excel(导出到浏览器,可以自定义工作表的大小)
* @param list      数据源
* @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
* @param sheetSize    每个工作表中记录的最大个数
* @param response  使用response可以导出到浏览器
* @throws ExcelException
*/
public static  <T>  void   listToExcel (
List<T> list ,
LinkedHashMap<String,String> fieldMap,
String sheetName,
int sheetSize,
HttpServletResponse response
) throws ExcelException{

//设置默认文件名为当前时间:年月日时分秒
String fileName=new SimpleDateFormat("yyyyMMddhhmmss").format(new Date()).toString();

//设置response头信息
response.reset();
response.setContentType("application/vnd.ms-excel");        //改成输出excel文件
response.setHeader("Content-disposition","attachment; filename="+fileName+".xls" );

//创建工作簿并发送到浏览器
try {

OutputStream out=response.getOutputStream();
listToExcel(list, fieldMap, sheetName, sheetSize,out );

} catch (Exception e) {
e.printStackTrace();

//如果是ExcelException,则直接抛出
if(e instanceof ExcelException){
throw (ExcelException)e;

//否则将其它异常包装成ExcelException再抛出
}else{
throw new ExcelException("导出Excel失败");
}
}
}

/**
* @MethodName  : listToExcel
* @Description : 导出Excel(导出到浏览器,工作表的大小是2003支持的最大值)
* @param list      数据源
* @param fieldMap      类的英文属性和Excel中的中文列名的对应关系
* @param response  使用response可以导出到浏览器
* @throws ExcelException
*/
public static <T>  void   listToExcel (
List<T> list ,
LinkedHashMap<String,String> fieldMap,
String sheetName,
HttpServletResponse response
) throws ExcelException{

listToExcel(list, fieldMap, sheetName, 65535, response);
}

/**
* @MethodName          : excelToList
* @Description             : 将Excel转化为List
* @param in                    :承载着Excel的输入流
* @param sheetIndex        :要导入的工作表序号
* @param entityClass       :List中对象的类型(Excel中的每一行都要转化为该类型的对象)
* @param fieldMap          :Excel中的中文列头和类的英文属性的对应关系Map
* @param uniqueFields  :指定业务主键组合(即复合主键),这些列的组合不能重复
* @return                      :List
* @throws ExcelException
*/
public static <T>  List<T>  excelToList(
InputStream in,
String sheetName,
Class<T> entityClass,
LinkedHashMap<String, String> fieldMap,
String[] uniqueFields
) throws ExcelException{

//定义要返回的list
List<T> resultList=new ArrayList<T>();

try {

//根据Excel数据源创建WorkBook
Workbook wb=Workbook.getWorkbook(in);
//获取工作表
Sheet sheet=wb.getSheet(sheetName);

//获取工作表的有效行数
int realRows=0;
for(int i=0;i<sheet.getRows();i++){

int nullCols=0;
for(int j=0;j<sheet.getColumns();j++){
Cell currentCell=sheet.getCell(j,i);
if(currentCell==null || "".equals(currentCell.getContents().toString())){
nullCols++;
}
}

if(nullCols==sheet.getColumns()){
break;
}else{
realRows++;
}
}

//如果Excel中没有数据则提示错误
if(realRows<=1){
throw new ExcelException("Excel文件中没有任何数据");
}

Cell[] firstRow=sheet.getRow(0);

String[] excelFieldNames=new String[firstRow.length];

//获取Excel中的列名
for(int i=0;i<firstRow.length;i++){
excelFieldNames[i]=firstRow[i].getContents().toString().trim();
}

//判断需要的字段在Excel中是否都存在
boolean isExist=true;
List<String> excelFieldList=Arrays.asList(excelFieldNames);
for(String cnName : fieldMap.keySet()){
if(!excelFieldList.contains(cnName)){
isExist=false;
break;
}
}

//如果有列名不存在,则抛出异常,提示错误
if(!isExist){
throw new ExcelException("Excel中缺少必要的字段,或字段名称有误");
}

//将列名和列号放入Map中,这样通过列名就可以拿到列号
LinkedHashMap<String, Integer> colMap=new LinkedHashMap<String, Integer>();
for(int i=0;i<excelFieldNames.length;i++){
colMap.put(excelFieldNames[i], firstRow[i].getColumn());
}

//判断是否有重复行
//1.获取uniqueFields指定的列
Cell[][] uniqueCells=new Cell[uniqueFields.length][];
for(int i=0;i<uniqueFields.length;i++){
int col=colMap.get(uniqueFields[i]);
uniqueCells[i]=sheet.getColumn(col);
}

//2.从指定列中寻找重复行
for(int i=1;i<realRows;i++){
int nullCols=0;
for(int j=0;j<uniqueFields.length;j++){
String currentContent=uniqueCells[j][i].getContents();
Cell sameCell=sheet.findCell(currentContent,
uniqueCells[j][i].getColumn(),
uniqueCells[j][i].getRow()+1,
uniqueCells[j][i].getColumn(),
uniqueCells[j][realRows-1].getRow(),
true);
if(sameCell!=null){
nullCols++;
}
}

if(nullCols==uniqueFields.length){
throw new ExcelException("Excel中有重复行,请检查");
}
}

//将sheet转换为list
for(int i=1;i<realRows;i++){
//新建要转换的对象
T entity=entityClass.newInstance();

//给对象中的字段赋值
for(Entry<String, String> entry : fieldMap.entrySet()){
//获取中文字段名
String cnNormalName=entry.getKey();
//获取英文字段名
String enNormalName=entry.getValue();
//根据中文字段名获取列号
int col=colMap.get(cnNormalName);

//获取当前单元格中的内容
String content=sheet.getCell(col, i).getContents().toString().trim();

//给对象赋值
setFieldValueByName(enNormalName, content, entity);
}

resultList.add(entity);
}
} catch(Exception e){
e.printStackTrace();
//如果是ExcelException,则直接抛出
if(e instanceof ExcelException){
throw (ExcelException)e;

//否则将其它异常包装成ExcelException再抛出
}else{
e.printStackTrace();
throw new ExcelException("导入Excel失败");
}
}
return resultList;
}

/*<-------------------------辅助的私有方法----------------------------------------------->*/
/**
* @MethodName  : getFieldValueByName
* @Description : 根据字段名获取字段值
* @param fieldName 字段名
* @param o 对象
* @return  字段值
*/
private static  Object getFieldValueByName(String fieldName, Object o) throws Exception{

Object value=null;
Field field=getFieldByName(fieldName, o.getClass());

if(field !=null){
field.setAccessible(true);
value=field.get(o);
}else{
throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 "+fieldName);
}

return value;
}

/**
* @MethodName  : getFieldByName
* @Description : 根据字段名获取字段
* @param fieldName 字段名
* @param clazz 包含该字段的类
* @return 字段
*/
private static Field getFieldByName(String fieldName, Class<?>  clazz){
//拿到本类的所有字段
Field[] selfFields=clazz.getDeclaredFields();

//如果本类中存在该字段,则返回
for(Field field : selfFields){
if(field.getName().equals(fieldName)){
return field;
}
}

//否则,查看父类中是否存在此字段,如果有则返回
Class<?> superClazz=clazz.getSuperclass();
if(superClazz!=null  &&  superClazz !=Object.class){
return getFieldByName(fieldName, superClazz);
}

//如果本类和父类都没有,则返回空
return null;
}

/**
* @MethodName  : getFieldValueByNameSequence
* @Description :
* 根据带路径或不带路径的属性名获取属性值
* 即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.name等
*
* @param fieldNameSequence  带路径的属性名或简单属性名
* @param o 对象
* @return  属性值
* @throws Exception
*/
private static  Object getFieldValueByNameSequence(String fieldNameSequence, Object o) throws Exception{

Object value=null;

//将fieldNameSequence进行拆分
String[] attributes=fieldNameSequence.split("\\.");
if(attributes.length==1){
value=getFieldValueByName(fieldNameSequence, o);
}else{
//根据属性名获取属性对象
Object fieldObj=getFieldValueByName(attributes[0], o);
String subFieldNameSequence=fieldNameSequence.substring(fieldNameSequence.indexOf(".")+1);
value=getFieldValueByNameSequence(subFieldNameSequence, fieldObj);
}
return value;

}

/**
* @MethodName  : setFieldValueByName
* @Description : 根据字段名给对象的字段赋值
* @param fieldName  字段名
* @param fieldValue    字段值
* @param o 对象
*/
private static void setFieldValueByName(String fieldName,Object fieldValue,Object o) throws Exception{

Field field=getFieldByName(fieldName, o.getClass());
if(field!=null){
field.setAccessible(true);
//获取字段类型
Class<?> fieldType = field.getType();

//根据字段类型给字段赋值
if (String.class == fieldType) {
field.set(o, String.valueOf(fieldValue));
} else if ((Integer.TYPE == fieldType)
|| (Integer.class == fieldType)) {
field.set(o, Integer.parseInt(fieldValue.toString()));
} else if ((Long.TYPE == fieldType)
|| (Long.class == fieldType)) {
field.set(o, Long.valueOf(fieldValue.toString()));
} else if ((Float.TYPE == fieldType)
|| (Float.class == fieldType)) {
field.set(o, Float.valueOf(fieldValue.toString()));
} else if ((Short.TYPE == fieldType)
|| (Short.class == fieldType)) {
field.set(o, Short.valueOf(fieldValue.toString()));
} else if ((Double.TYPE == fieldType)
|| (Double.class == fieldType)) {
field.set(o, Double.valueOf(fieldValue.toString()));
} else if (Character.TYPE == fieldType) {
if ((fieldValue!= null) && (fieldValue.toString().length() > 0)) {
field.set(o, Character
.valueOf(fieldValue.toString().charAt(0)));
}
}else if(Date.class==fieldType){
field.set(o, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(fieldValue.toString()));
}else{
field.set(o, fieldValue);
}
}else{
throw new ExcelException(o.getClass().getSimpleName() + "类不存在字段名 "+fieldName);
}
}

/**
* @MethodName  : setColumnAutoSize
* @Description : 设置工作表自动列宽和首行加粗
* @param ws
*/
private static void setColumnAutoSize(WritableSheet ws,int extraWith){
//获取本列的最宽单元格的宽度
for(int i=0;i<ws.getColumns();i++){
int colWith=0;
for(int j=0;j<ws.getRows();j++){
String content=ws.getCell(i,j).getContents().toString();
int cellWith=content.length();
if(colWith<cellWith){
colWith=cellWith;
}
}
//设置单元格的宽度为最宽宽度+额外宽度
ws.setColumnView(i, colWith+extraWith);
}

}

/**
* @MethodName  : fillSheet
* @Description : 向工作表中填充数据
* @param sheet     工作表
* @param list  数据源
* @param fieldMap 中英文字段对应关系的Map
* @param firstIndex    开始索引
* @param lastIndex 结束索引
*/
private static <T> void fillSheet(
WritableSheet sheet,
List<T> list,
LinkedHashMap<String,String> fieldMap,
int firstIndex,
int lastIndex
)throws Exception{

//定义存放英文字段名和中文字段名的数组
String[] enFields=new String[fieldMap.size()];
String[] cnFields=new String[fieldMap.size()];

//填充数组
int count=0;
for(Entry<String,String> entry:fieldMap.entrySet()){
enFields[count]=entry.getKey();
cnFields[count]=entry.getValue();
count++;
}
//填充表头
for(int i=0;i<cnFields.length;i++){
Label label=new Label(i,0,cnFields[i]);
sheet.addCell(label);
}

//填充内容
int rowNo=1;
for(int index=firstIndex;index<=lastIndex;index++){
//获取单个对象
T item=list.get(index);
for(int i=0;i<enFields.length;i++){
Object objValue=getFieldValueByNameSequence(enFields[i], item);
String fieldValue=objValue==null ? "" : objValue.toString();
Label label =new Label(i,rowNo,fieldValue);
sheet.addCell(label);
}

rowNo++;
}

//设置自动列宽
setColumnAutoSize(sheet, 5);
}

}


导入数据:

@Action
public void showInputSalesData() {
{
FileOutputStream fos = null;
try {
jProgressBar_data.setValue(0);
Date startDate = jIDatePanel_startDate.getDate();
Date endDate = jIDatePanel_endDate.getDate();
if (startDate == null || endDate == null) {
AppMain.showError("请选择日期");
return;
}
String start = Pub.formatDate(startDate);
String end = Pub.formatDate(endDate);
jProgressBar_data.setMinimum(0);
List<K3SalesInvoice> datas = EJBClientRemote.getK3CommonSessionBeanRemote().findAllSalesInvoice(start, end);
System.out.print("datas.size()==" + datas.size());
jProgressBar_data.setMaximum(datas.size() - 1);
if (datas == null || datas.size() <= 0) {
AppMain.showError("没有数据");
return;
}

FileDialog dialog = new FileDialog();
int index = dialog.showSaveDialog(this);
if (index == 1) {
return;
}
String filename = dialog.getSelectedFile().getAbsolutePath();
if (!filename.endsWith("xls")) {
filename += ".xls";
}
File file = new File(filename);
LinkedHashMap<String, String> fieldMap = new LinkedHashMap<String, String>();
fieldMap.put("fdate", "日期");
fieldMap.put("fbillno", "单号");
fieldMap.put("accuontNO", "客户代码");
fieldMap.put("currency", "币别");
fieldMap.put("fExchangeRate", "汇率");
fieldMap.put("fTaxRate", "税率");
fieldMap.put("materialCode", "物料代码");
fieldMap.put("number", "数量");
fieldMap.put("price", "单价");
fieldMap.put("amount", "金额");
fieldMap.put("voucher", "制单");
fieldMap.put("department", "部门");
fieldMap.put("salesMan", "业务员");
fieldMap.put("subjects", "往来科目");
fieldMap.put("note", "摘要");
if (!file.exists()) {
//file.mkdirs();
file.createNewFile();

}
fos = new FileOutputStream(file);
//设置监听
ExcelUtil.setOnProgressListener(this);
ExcelUtil.listToExcel(datas, fieldMap, null, fos);
} catch  (IOException ex) {
Logger.getLogger(K3DataInputAndOutput_Panel.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException ex) {
Logger.getLogger(K3DataInputAndOutput_Panel.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExcelException ex) {
Logger.getLogger(K3DataInputAndOutput_Panel.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException ex) {
Logger.getLogger(K3DataInputAndOutput_Panel.class.getName()).log(Level.SEVERE, null, ex);
}
}
}

}


该工具类有4个重载的导出方法和1个导入方法,大家可以根据实际情况进行选择。

总结

导入和导出方法都是通过传一个fieldMap参数(类的英文属性和Excel的中文列头的对应关系)来连接实体类和Excel的

导出的时候可以选择导出到本地文件系统或导出到浏览器,也可以自定义每个工作表的大小

导入的时候可以自定义业务主键组合uniqueFields,这样就可以检测Excel中是否有重复行了

—————————————————————————————————————————————————–

java架构师项目实战,高并发集群分布式,大数据高可用视频教程,共760G

下载地址:

https://item.taobao.com/item.htm?id=562739965822


01.高级架构师四十二个阶段高

02.Java高级系统培训架构课程148课时

03.Java高级互联网架构师课程

04.Java互联网架构Netty、Nio、Mina等-视频教程

05.Java高级架构设计2016整理-视频教程

06.架构师基础、高级片

07.Java架构师必修linux运维系列课程

08.Java高级系统培训架构课程116课时

+

hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门

内容详情:

【入门篇】

J2SE的Socket网络编程应用

J2SE的反射机制高级应用

J2SE高深讲解

JAVA编程思想 中级教程

JAVA编程思想 初级教程

JAVA编程思想 高级教程

基于J2SE的QQ聊天工具开发

我来说说面向对象

【进阶篇】

CRM项目

Eclipse

Hibernate

JAVA WEB开发视频

JAVAWEB开发实战经典-高级案例篇

JAVAWEB

JAVA线程并发教程

java网上在线支付实战视频

java设计模式

jdbc

junit

mybatis

spring mvc

SpringMvc+Spring+MyBatis+Maven整合视频

SpringMVC

Spring

Struts2 开发实战讲解

Struts2+Spring3+Hibernate4+Maven+EasyUI整合入门视频

Struts

SVN

tomcat

weblogic

WebService从入门到上手企业开发

企业系统OA办公自动化

手机进销存系统

数据结构和算法视频

设计模式系列

【高级篇】

Cas单点登录

Extjs4.1+Spring3.2+hibernate4.1+MySql5商城

Git权威指南

groovy入门视频

Java 8新特性

Lucene

Mongodb

node.js

Nutch相关框架

OA办公自动化系统

Quartz Job定时任务

Solr高级应用视频

Spring Security权限控制

Spring源码解读与设计详析

Struts2源码剖析与架构指导

大型CMS内容管理系统项目

微信入门视频

深入JVM内核—原理、诊断与优化

深入浅出微信公众平台实战开发(微网站、LBS云、Api接口调用、服务号高级接口)

银行接口资料

【架构篇】

ActiveMQ实战

Apache-Tomcat集群搭建

Linux集群

Linux高级架构架构方案及实现指南

Memcached分布式集群

Mysql特级优化课程

Nginx+Tomcat+Memcached群集配置软件包

Nginx服务器搭建

Nginx网站架构实战(Web服务器负载均衡与反向代理)

SOA Dubbo

storm入门到精通

storm集群的搭建

storm项目实战

UML建模

互联网公司技术架构系列

京东B2C平台推荐搜索的实践和思考

京东大数据分析与创新应用

京东大规模存储持续研发

京东电商海量订单处理OFC系统的关键技术

优米网架构设计方案

基于SOA 思想下的百万数据架构

大型网站提速之MySQL优化

大型网站架构设计

大数据高并发架构实战案例

数据优化技术Redis

数据库高并发原理

深入Java程序性能调优

深入浅出MongoDB应用实战集群及系统架构

深度揭秘服务器端内幕

电商网站之Solr应用

系统架构设计师

阿里分布式数据库服务实践

—————————————————————————————————————————————————–
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 异常 数据 excel