Java基础学习总结(49)——Excel导入导出工具类

时间:2021-01-19 06:58:22

在项目的pom文件中引入

<dependency>

<groupId>net.sourceforge.jexcelapi</groupId>

<artifactId>jxl</artifactId>

<version>2.6.10</version> </dependency>

package com.zhy.util.excel;

import java.io.InputStream;

import java.io.OutputStream;

import java.lang.reflect.Field;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Date;

import java.util.LinkedHashMap;

import java.util.List;

import java.util.Map.Entry;





import javax.servlet.http.HttpServletResponse;





import jxl.Cell;

import jxl.Sheet;

import jxl.Workbook;

import jxl.write.Label;

import jxl.write.WritableSheet;

import jxl.write.WritableWorkbook;





/**

 * @ClassName: ExcelUtil

 * @Description: TODO(Excel导入导出工具类:导出就是将List转化为Excel(listToExcel)

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

 * @author zhanghaiyang

 * @date 2016年2月18日 下午1:29:05

 * @Copyright © 2016上海通善互联网金融信息服务有限公司

 */

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

}

}