package com.liyang.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFPatriarch;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* POI操作EXCEL工具类
*/
public class ExcelUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelUtil.class);
/**
* Excel 97-2003 (.xls)
*/
public static final int XLS = 0;
/**
* Excel (.xlsx)
*/
public static final int XLSX = 1;
public static final short HEADER_BACKGROUND_COLOR = HSSFColor.PALE_BLUE.index;
public static final short HEADER_FONT_COLOR = HSSFColor.DARK_RED.index;
public static final short DEFAULT_ROW_HEIGHT = 500;
public static final short DEFAULT_COLUMN_WIDTH = 20 * 256;
public static final String FILE_NOT_FOUND_EXCEPTION = "路径指定的文件不存在";
public static final String BAD_FILE_FORMAT_EXCEPTION = "Excel文件格式不正确";
public static final String COLUMN_COUNT_NOT_SAME = "单行列数不一致";
/**
* 打开指定路径文件
* @param path EXCEL文件路径
* @return {@link Workbook}
* @author liyang
*/
public static Workbook open(String path) {
String suffix = path.substring(path.lastIndexOf(".")).toLowerCase();
if (!".xls".equals(suffix) && !".xlsx".equals(suffix)) {
throw new RuntimeException(BAD_FILE_FORMAT_EXCEPTION);
}
InputStream in = null;
Workbook workbook = null;
try {
in = new FileInputStream(new File(path));
if (".xls".equals(suffix)) {
workbook = new HSSFWorkbook(in);
} else {
workbook = new XSSFWorkbook(in);
}
} catch (Exception e) {
LOGGER.error("打开文档失败", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return workbook;
}
/**
* 读取EXCEL文件获得对象列表
* @param path EXCEL文件路径
* @param clazz 获得的对象类型{@link Class}
* @param firstRowIndex 起始行索引,从0开始
* @param lastRowIndex 结束行索引,从0开始
* @return
* @author liyang
*/
public static List<Object> xlsDto(String path, Class<?> clazz,
int firstRowIndex, int lastRowIndex) {
String suffix = path.substring(path.lastIndexOf(".")).toLowerCase();
if (!".xls".equals(suffix) && !".xlsx".equals(suffix)) {
throw new RuntimeException(BAD_FILE_FORMAT_EXCEPTION);
}
List<Object> list = null;
InputStream in = null;
try {
in = new FileInputStream(new File(path));
int fileType;
if (".xls".equals(suffix)) {
fileType = ExcelUtil.XLS;
} else {
fileType = ExcelUtil.XLSX;
}
list = xlsDto(in, fileType, clazz, firstRowIndex, lastRowIndex);
} catch (Exception e) {
LOGGER.error("读取EXCEL文件获得对象列表失败", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return list;
}
/**
* 读取EXCEL文件获得对象列表
* @param inputStream {@link InputStream}输入流
* @param fileType {@link ExcelUtil#XLS},{@link ExcelUtil#XLSX}
* @param clazz 获得的对象类型{@link Class}
* @param firstRowIndex 起始行索引,从0开始
* @param lastRowIndex 结束行索引,从0开始
* @return
* @author liyang
*/
public static List<Object> xlsDto(InputStream inputStream, int fileType, Class<?> clazz,
int firstRowIndex, int lastRowIndex) {
if (XLS != fileType && XLSX != fileType) {
throw new RuntimeException(BAD_FILE_FORMAT_EXCEPTION);
}
List<Object> list = new ArrayList<Object>();
Workbook workbook = null;
try {
if (XLS == fileType) {
workbook = new HSSFWorkbook(inputStream);
} else {
workbook = new XSSFWorkbook(inputStream);
}
// java反射获得类的属性及类型
Field[] fields = clazz.getDeclaredFields();
String[] fieldNames = new String[fields.length];
Class<?>[] fieldTypes = new Class<?>[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldNames[i] = fields[i].getName();
fieldTypes[i] = fields[i].getType();
}
Sheet sheet = workbook.getSheetAt(0);
Row row;
Cell cell;
Object object;
Method method;
for (int rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) {
row = sheet.getRow(rowIndex);
// 创建一个新对象
object = Class.forName(clazz.getName()).newInstance();
for (int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
cell = row.getCell(fieldIndex);
// java反射调用属性的set方法给属性赋值
method = clazz.getMethod("set"
+ fieldNames[fieldIndex].substring(0, 1).toUpperCase()
+ fieldNames[fieldIndex].substring(1), fieldTypes[fieldIndex]);
method.invoke(object, getCellValue(cell, fieldTypes[fieldIndex].getName()));
}
list.add(object);
}
} catch (Exception e) {
LOGGER.error("读取EXCEL文件获得对象列表失败", e);
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return list;
}
/**
* 读取EXCEL文件获得String[][]数组
* @param path Excel文件路径
* @param firstRowIndex 起始行索引,从0开始
* @param lastRowIndex 结束行索引,从0开始
* @param firstColIndex 起始列索引,从0开始
* @param lastColIndex 结束列索引,从0开始
* @return
* @author liyang
*/
public static String[][] xlsToStringArray(String path, int firstRowIndex, int lastRowIndex, int firstColIndex, int lastColIndex) {
String suffix = path.substring(path.lastIndexOf(".")).toLowerCase();
if (!".xls".equals(suffix) && !".xlsx".equals(suffix)) {
throw new RuntimeException(BAD_FILE_FORMAT_EXCEPTION);
}
String[][] array = new String[0][0];
Workbook workbook = null;
try {
workbook = open(path);
array = xlsToStringArray(workbook, firstRowIndex, lastRowIndex, firstColIndex, lastColIndex);
} catch (Exception e) {
LOGGER.error("读取EXCEL文件获得String[][]数组失败", e);
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return array;
}
/**
* 读取EXCEL文件获得String[][]数组
* @param bytes Excel文件内容
* @param fileType {@link ExcelUtil#XLS}, {@link ExcelUtil#XLSX}
* @param firstRowIndex 起始行索引,从0开始
* @param lastRowIndex 结束行索引,从0开始
* @param firstColIndex 起始列索引,从0开始
* @param lastColIndex 结束列索引,从0开始
* @return
* @author liyang
*/
public static String[][] xlsToStringArray(byte[] bytes, int fileType, int firstRowIndex, int lastRowIndex, int firstColIndex, int lastColIndex) {
if (XLS != fileType && XLSX != fileType) {
throw new RuntimeException(BAD_FILE_FORMAT_EXCEPTION);
}
String[][] array = new String[0][0];
InputStream is = null;
Workbook workbook = null;
try {
is = new ByteArrayInputStream(bytes);
if (XLS == fileType) {
workbook = new HSSFWorkbook(is);
} else {
workbook = new XSSFWorkbook(is);
}
array = xlsToStringArray(workbook, firstRowIndex, lastRowIndex, firstColIndex, lastColIndex);
} catch (IOException e) {
LOGGER.error("读取EXCEL文件获得String[][]数组失败", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return array;
}
/**
* 导出EXCEL文件到response流
* @param response {@link HttpServletResponse}
* @param filename 文件名(不包含后缀)
* @param encoding 字符编码
* @param sheetName sheet表名
* @param headers 标题行(String[])
* @param dataSet 数据(Collection<?>)
* @param dateFormat 由yyyy年、MM月、dd日、HH小时、mm分钟、ss秒构成的字符串
* @author liyang
*/
public static void export(HttpServletResponse response, String filename, String encoding,
String sheetName, String[] headers, Collection<?> dataSet, String dateFormat) {
try {
resetResponseToDownload(response, filename, encoding);
} catch (UnsupportedEncodingException e) {
LOGGER.error("文件名转码失败", e);
return;
}
OutputStream out = null;
try {
out = response.getOutputStream();
export(out, sheetName, headers, dataSet, dateFormat);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到response流失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL文件到指定路径
* @param path 导出路径
* @param sheetName sheet表名
* @param headers 标题行(String[])
* @param dataSet 数据(Collection<?>)
* @param dateFormat 由yyyy年、MM月、dd日、HH小时、mm分钟、ss秒构成的字符串
* @author liyang
*/
public static void export(String path, String sheetName, String[] headers, Collection<?> dataSet, String dateFormat) {
OutputStream out = null;
try {
File file = createFile(path);
out = new FileOutputStream(file);
export(out, sheetName, headers, dataSet, dateFormat);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到指定路径失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL文件到response流
* @param response {@link HttpServletResponse}
* @param filename 文件名(不包含后缀)
* @param encoding 字符编码
* @param sheetName sheet表名
* @param headers 标题行(String[])
* @param data 数据(String[][])
* @author liyang
*/
public static void export(HttpServletResponse response, String filename, String encoding,
String sheetName, String[] headers, String[][] data) {
try {
resetResponseToDownload(response, filename, encoding);
} catch (UnsupportedEncodingException e) {
LOGGER.error("文件名转码失败", e);
return;
}
OutputStream out = null;
try {
out = response.getOutputStream();
export(out, sheetName, headers, data);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到response流失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL文件到指定路径
* @param path 导出路径
* @param sheetName sheet表名
* @param headers 标题行(String[])
* @param data 数据(String[][])
* @author liyang
*/
public static void export(String path, String sheetName, String[] headers, String[][] data) {
OutputStream out = null;
try {
File file = createFile(path);
out = new FileOutputStream(file);
export(out, sheetName, headers, data);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到指定路径失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL文件到response流
* @param response {@link HttpServletResponse}
* @param filename 文件名(不包含后缀)
* @param encoding 字符编码
* @param sheetName sheet表名
* @param headers 标题行(String[][])
* @param dataSet 数据(Collection<?>)
* @param dateFormat 由yyyy年、MM月、dd日、HH小时、mm分钟、ss秒构成的字符串
* @author liyang
*/
public static void export(HttpServletResponse response, String filename, String encoding,
String sheetName, String[][] headers, Collection<?> dataSet, String dateFormat) {
try {
resetResponseToDownload(response, filename, encoding);
} catch (UnsupportedEncodingException e) {
LOGGER.error("文件名转码失败", e);
return;
}
OutputStream out = null;
try {
out = response.getOutputStream();
export(out, sheetName, headers, dataSet, dateFormat);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到response流失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL文件到指定路径
* @param path 导出路径
* @param sheetName sheet表名
* @param headers 标题行(String[][])
* @param dataSet 数据(Collection<?>)
* @param dateFormat 由yyyy年、MM月、dd日、HH小时、mm分钟、ss秒构成的字符串
* @author liyang
*/
public static void export(String path, String sheetName, String[][] headers, Collection<?> dataSet, String dateFormat) {
OutputStream out = null;
try {
File file = createFile(path);
out = new FileOutputStream(file);
export(out, sheetName, headers, dataSet, dateFormat);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到指定路径失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL文件到response流
* @param response {@link HttpServletResponse}
* @param filename 文件名(不包含后缀)
* @param encoding 字符编码
* @param sheetName sheet表名
* @param headers 标题行(String[][])
* @param data 数据(String[][])
* @author liyang
*/
public static void export(HttpServletResponse response, String filename, String encoding,
String sheetName, String[][] headers, String[][] data) {
try {
resetResponseToDownload(response, filename, encoding);
} catch (UnsupportedEncodingException e) {
LOGGER.error("文件名转码失败", e);
return;
}
OutputStream out = null;
try {
out = response.getOutputStream();
export(out, sheetName, headers, data);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到response流失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL文件到指定路径
* @param path 导出路径
* @param sheetName sheet表名
* @param headers 标题行(String[][])
* @param data 数据(String[][])
* @author liyang
*/
public static void export(String path, String sheetName, String[][] headers, String[][] data) {
OutputStream out = null;
try {
File file = createFile(path);
out = new FileOutputStream(file);
export(out, sheetName, headers, data);
out.flush();
} catch (IOException e) {
LOGGER.error("导出EXCEL文件到指定路径失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 将工作簿导出至指定路径
* @param workbook {@link Workbook}
* @param destPath 指定路径
* @author liyang
*/
public static void export(Workbook workbook, String destPath) {
OutputStream out = null;
try {
File file = createFile(destPath);
out = new FileOutputStream(file);
workbook.write(out);
out.flush();
} catch (IOException e) {
LOGGER.error("将工作簿导出至指定路径失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 将工作簿导出至response流
* @param workbook {@link Workbook}
* @param response {@link HttpServletResponse}
* @param filename 文件名(不含后缀)
* @param encoding 字符编码
* @author liyang
*/
public static void export(Workbook workbook, HttpServletResponse response, String filename, String encoding) {
try {
resetResponseToDownload(response, filename, encoding);
} catch (UnsupportedEncodingException e) {
LOGGER.error("文件名转码失败", e);
return;
}
OutputStream out = null;
try {
out = response.getOutputStream();
workbook.write(out);
out.flush();
} catch (Exception e) {
LOGGER.error("将工作簿导出至response流失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 打开指定模板填充数据并导出至文件
* @param templatePath 模板路径
* @param sheetName sheet表名
* @param lineCnt 数据区域总行数
* @param colCnt 数据区域总列数
* @param firstRowIndex 数据起始行索引,从0开始
* @param firstColIndex 数据起始列索引,从0开始
* @param data double[][]型数据
* @param decimal 小数位
* @param reserveHidden 是否保留单元格隐藏内容("$"及其后的内容)
* @param destPath 导出文件路径
* @author liyang
*/
public static void export(String templatePath, String sheetName, int lineCnt, int colCnt,
int firstRowIndex, int firstColIndex, String[][] data, int decimal, boolean reserveHidden, String destPath) {
// 复制模板到目的路径
if (!templatePath.equals(destPath)) {
try {
File file = createFile(destPath);
FileUtils.copyFile(new File(templatePath), file);
} catch (IOException e) {
LOGGER.error("复制模板失败", e);
}
}
Workbook workbook = null;
OutputStream out = null;
try {
workbook = open(destPath);
Sheet sheet = workbook.getSheet(sheetName);
Row row;
Cell cell;
String value;
for (int i = 0; i < lineCnt; i++) {
row = sheet.getRow(i + firstRowIndex);
for (int j = 0; j < colCnt; j++) {
cell = row.getCell(j + firstColIndex);
value = data[i][j];
if (StringUtils.isNumeric(value)) { // 数字
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
if (value.contains(".")) { // 小数
cell.setCellValue(Double.parseDouble(value));
} else { // 整数
cell.setCellValue(Integer.parseInt(value));
}
} else { // 字符串
if (!reserveHidden && value.contains("$")) {
value = value.substring(0, value.indexOf("$"));
}
cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue(value);
}
}
}
out = new FileOutputStream(new File(destPath));
workbook.write(out);
out.flush();
} catch (IOException e) {
LOGGER.error("导出至文件失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 打开指定模板填充数据并导出至文件
* @param path 导出文件路径
* @param templatePath 模板路径
* @param sheetName sheet表名
* @param lineCnt 数据区域总行数
* @param colCnt 数据区域总列数
* @param firstRowIndex 数据起始行索引,从0开始
* @param firstColIndex 数据起始列索引,从0开始
* @param data double[][]型数据
* @param decimal 小数位
* @author liyang
*/
public static void export(String path, String templatePath, String sheetName,
int lineCnt, int colCnt, int firstRowIndex, int firstColIndex, String[][] data, int decimal) {
Workbook workbook = null;
OutputStream out = null;
try {
workbook = open(templatePath);
Sheet sheet = workbook.getSheet(sheetName);
Row row;
Cell cell;
String value;
for (int i = 0; i < lineCnt; i++) {
row = sheet.getRow(i + firstRowIndex);
for (int j = 0; j < colCnt; j++) {
cell = row.getCell(j + firstColIndex);
value = data[i][j];
if (StringUtils.isNumeric(value)) { // 数字
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
if (value.contains(".")) { // 小数
cell.setCellValue(Double.parseDouble(value));
} else { // 整数
cell.setCellValue(Integer.parseInt(value));
}
} else { // 字符串
value = value.substring(0, value.indexOf("$"));
cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue(value);
}
}
}
File file = createFile(path);
out = new FileOutputStream(file);
workbook.write(out);
out.flush();
} catch (IOException e) {
LOGGER.error("写入文件失败", e);
} finally {
try {
out.close();
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 打开指定模板填充数据并导出至response流
* @param response {@link HttpServletResponse}
* @param filename 文件名(不包含后缀)
* @param encoding 字符编码
* @param templatePath 模板路径
* @param sheetName sheet表名
* @param lineCnt 数据区域总行数
* @param colCnt 数据区域总列数
* @param firstRowIndex 数据起始行索引,从0开始
* @param firstColIndex 数据起始列索引,从0开始
* @param data double[][]型数据
* @param decimal 小数位
* @author liyang
*/
public static void export(HttpServletResponse response, String filename, String encoding,
String templatePath, String sheetName, int lineCnt, int colCnt, int firstRowIndex, int firstColIndex,
String[][] data, int decimal) {
try {
resetResponseToDownload(response, filename, encoding);
} catch (UnsupportedEncodingException e) {
LOGGER.error("文件名转码失败", e);
return;
}
Workbook workbook = null;
OutputStream out = null;
try {
workbook = open(templatePath);
Sheet sheet = workbook.getSheet(sheetName);
Row row;
Cell cell;
String value;
for (int i = 0; i < lineCnt; i++) {
row = sheet.getRow(i + firstRowIndex);
for (int j = 0; j < colCnt; j++) {
cell = row.getCell(j + firstColIndex);
value = data[i][j];
if (StringUtils.isNumeric(value)) { // 数字
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
if (value.contains(".")) { // 小数
cell.setCellValue(Double.parseDouble(value));
} else { // 整数
cell.setCellValue(Integer.parseInt(value));
}
} else { // 字符串
value = value.substring(0, value.indexOf("$"));
cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue(value);
}
}
}
out = response.getOutputStream();
workbook.write(out);
out.flush();
} catch (IOException e) {
LOGGER.error("写入response流失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 合并单元格
* @param sheet {@link Sheet}
* @param regionArray 合并区域数组,region形如"A1:E3"表示合并从A1至E3的区域
* @author liyang
*/
public static void merge(Sheet sheet, String[] regionArray) {
for (String region : regionArray) {
sheet.addMergedRegion(formatCellRangeAddress(region));
}
}
/**
* 合并单元格
* @param sheet {@link Sheet}
* @param regionArray 合并区域数组,region形如"A1:E3"表示合并从A1至E3的区域
* @author liyang
*/
public static void merge(Sheet sheet, CellRangeAddress[] regionArray) {
for (CellRangeAddress region : regionArray) {
sheet.addMergedRegion(region);
}
}
/**
* 获取单元格的值
* @param cell {@link Cell}
* @param type {@link Class#getName()}
* @return
* @author liyang
*/
public static Object getCellValue(Cell cell, String type) {
if ("char".equals(type) || "java.lang.String".equals(type)) {
cell.setCellType(Cell.CELL_TYPE_STRING);
return cell.getStringCellValue();
}
if ("short".equals(type) || "java.lang.Short".equals(type)) {
cell.setCellType(Cell.CELL_TYPE_STRING);
String value = cell.getStringCellValue();
return Short.parseShort(value);
}
if ("int".equals(type) || "java.lang.Integer".equals(type)) {
cell.setCellType(Cell.CELL_TYPE_STRING);
String value = cell.getStringCellValue();
return Integer.parseInt(value);
}
if ("long".equals(type) || "java.lang.Long".equals(type)) {
cell.setCellType(Cell.CELL_TYPE_STRING);
String value = cell.getStringCellValue();
return Long.parseLong(value);
}
if ("float".equals(type) || "java.lang.Float".equals(type)) {
Double value = cell.getNumericCellValue();
return Float.parseFloat(value.toString());
}
if ("double".equals(type) || "java.lang.Double".equals(type)) {
return cell.getNumericCellValue();
}
if ("boolean".equals(type) || "java.lang.Boolean".equals(type)) {
return cell.getBooleanCellValue();
}
if ("java.util.Date".equals(type)) {
return cell.getDateCellValue();
}
return null;
}
/**
* 获取单元格的值
* @param cell {@link Cell}
* @return
*/
public static String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
String value = cell.getStringCellValue();
return value;
}
if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
String value = String.valueOf(cell.getBooleanCellValue());
return value;
}
if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
String value = cell.getCellFormula() ;
return value;
}
if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
String value = String.valueOf(cell.getNumericCellValue());
return value;
}
return "";
}
/**
* 获取单元格的值
* @param sheet {@link sheet}
* @param rowIndex 行索引
* @param colIndex 列索引
* @return
*/
public static String getCellValue(Sheet sheet, int rowIndex, int colIndex) {
Row row = sheet.getRow(rowIndex);
if (row == null) {
return "";
}
Cell cell = row.getCell(colIndex);
if (cell == null) {
return "";
}
String value = getCellValue(cell);
return value;
}
/**
* 复制单元格
* @param srcCell 源单元格
* @param destCell 目标单元格
* @param onlyStyle 仅复制样式
* @author liyang
*/
public static void copyCell(Cell srcCell, Cell destCell, boolean onlyStyle) {
destCell.setCellStyle(srcCell.getCellStyle());
int cellType = srcCell.getCellType();
destCell.setCellType(cellType);
if (!onlyStyle) {
switch (cellType) {
case Cell.CELL_TYPE_BOOLEAN:
destCell.setCellValue(srcCell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_ERROR:
destCell.setCellErrorValue(srcCell.getErrorCellValue());
break;
case Cell.CELL_TYPE_FORMULA:
destCell.setCellFormula(parseFormula(srcCell.getCellFormula()));
break;
case Cell.CELL_TYPE_NUMERIC:
destCell.setCellValue(srcCell.getNumericCellValue());
break;
case Cell.CELL_TYPE_STRING:
destCell.setCellValue(srcCell.getRichStringCellValue());
break;
}
}
}
/**
* 复制行
* @param srcRow 源行
* @param destRow 目标行
* @param colCnt 列数
* @param onlyStyle 仅复制样式
* @author liyang
*/
public static void copyRow(Row srcRow, Row destRow, int colCnt, boolean onlyStyle) {
destRow.setHeight(srcRow.getHeight());
Cell srcCell, destCell;
for (int i = 0; i < colCnt; i++) {
srcCell = srcRow.getCell(i);
if (srcCell == null) {
continue;
}
destCell = destRow.getCell(i);
if (destCell == null) {
destCell = destRow.createCell(i);
}
copyCell(srcCell, destCell, onlyStyle);
}
}
/**
* 插入行
* @param sheet sheet表对象
* @param insertLineIndex 插入行索引,从0开始
* @param styleRow 样式行对象,新插入行将会复制styleRow的样式
* @param styleRowColCnt 样式行总列数
* @author liyang
*/
public static void insertRow(Sheet sheet, int insertLineIndex, Row styleRow, int styleRowColCnt) {
sheet.shiftRows(insertLineIndex, sheet.getLastRowNum(), 1, false, false);
Row row = (Row)sheet.createRow(insertLineIndex);
copyRow(styleRow, row, styleRowColCnt, true);
}
/**
* 复制sheet
* @param srcSheet 源sheet
* @param destSheet 目标sheet
* @param colCnt 列数
* @author liyang
*/
public static void copySheet(Sheet srcSheet, Sheet destSheet, int colCnt) {
// 复制行
Row srcRow, destRow;
for (int i = srcSheet.getFirstRowNum(); i <= srcSheet.getLastRowNum(); i++) {
srcRow = srcSheet.getRow(i);
destRow = srcSheet.createRow(i);
copyRow(srcRow, destRow, colCnt, false);
}
// 复制合并单元格
for (int i = 0; i < srcSheet.getNumMergedRegions(); i++) {
destSheet.addMergedRegion(srcSheet.getMergedRegion(i));
}
}
/**
* POI的bug:如果公式里面的函数不带参数,比如now()或today(), 则通过getCellFormula()取出来的值为
* now(ATTR(semiVolatile))和today(ATTR(semiVolatile)),这样的值写入Excel时会出错。该方法的功能很简
* 单,就是把ATTR(semiVolatile)删掉。
* @param formula 公式
* @return
* @author liyang
*/
public static String parseFormula(String formula) {
final String REPLACE_STR = "ATTR(semiVolatile)";
formula = formula.replaceAll(REPLACE_STR, "");
return formula;
}
/**
* 把形如"A1:E3"形式的合并区域格式化成CellRangeAddress
* @param region
* @author liyang
*/
public static CellRangeAddress formatCellRangeAddress(String region) {
int index = region.indexOf(":");
int colStart = (int)(region.charAt(0)) - (int)('A');
int colEnd = (int)(region.charAt(index + 1)) - (int)('A');
int rowStart = Integer.parseInt(region.substring(1, index));
int rowEnd = Integer.parseInt(region.substring(index + 2));
CellRangeAddress cra = new CellRangeAddress(rowStart, rowEnd, colStart, colEnd);
return cra;
}
/**
* 插入图片
* @param excelPath excel文件路径
* @param sheetName sheet表名
* @param imageBytes 图片二进制文件
* @param firstRowIndex 起始行,从0开始
* @param lastRowIndex 结束行,从0开始
* @param firstColIndex 起始列,从0开始
* @param lastColIndex 结束列,从0开始
* @author liyang
*/
public static void insertImage(String excelPath, String sheetName, byte[] imageBytes,
int firstRowIndex, int lastRowIndex, int firstColIndex, int lastColIndex) {
Workbook workbook = null;
OutputStream out = null;
try {
workbook = open(excelPath);
HSSFSheet sheet = (HSSFSheet)workbook.getSheet(sheetName);
// 画图的顶级管理器,一个sheet只能获取一个(一定要注意这点)
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
// anchor主要用于设置图片的属性
HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 255, (short)firstColIndex, firstRowIndex, (short)lastColIndex, lastRowIndex);
anchor.setAnchorType(AnchorType.MOVE_DONT_RESIZE);
// 插入图片
patriarch.createPicture(anchor, workbook.addPicture(imageBytes, HSSFWorkbook.PICTURE_TYPE_JPEG));
out = new FileOutputStream(excelPath);
workbook.write(out);
out.flush();
} catch (IOException e) {
LOGGER.error("写入文件失败", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 插入图片
* @param excelPath excel文件路径
* @param sheetName sheet表名
* @param imagePath 图片文件路径
* @param firstRowIndex 起始行,从0开始
* @param lastRowIndex 结束行,从0开始
* @param firstColIndex 起始列,从0开始
* @param lastColIndex 结束列,从0开始
* @author liyang
*/
public static void insertImage(String excelPath, String sheetName, String imagePath,
int firstRowIndex, int lastRowIndex, int firstColIndex, int lastColIndex) {
byte[] bytes = null;
try {
bytes = FileUtils.readFileToByteArray(new File(imagePath));
} catch (IOException e) {
LOGGER.error("读取文件失败", e);
}
if (bytes != null && bytes.length > 0) {
insertImage(excelPath, sheetName, bytes, firstColIndex, firstRowIndex, lastColIndex, lastRowIndex);
}
}
/**
* 导出EXCEL到指定IO设备
* @param out {@link OutputStream}
* @param sheetName sheet表名称
* @param headers 标题行(String[])
* @param data 数据(String[][] data)
* @throws IOException
* @author liyang
*/
private static void export(OutputStream out, String sheetName, String[] headers, String[][] data) throws IOException {
Workbook workbook = null;
try {
// 声明一个工作薄
workbook = new HSSFWorkbook();
// 生成一个表格
Sheet sheet = workbook.createSheet(sheetName);
// 生成标题样式
CellStyle headStyle = getHeadStyle(workbook);
// 生成记录样式
CellStyle bodyStyle = getBodyStyle(workbook);
int i = 0, colCnt = 0;
// 产生表格标题行
if (headers != null) {
Row row = sheet.createRow(i++);
row.setHeight(DEFAULT_ROW_HEIGHT);
Cell cell;
HSSFRichTextString text;
for (int j = 0; j < headers.length; j++) {
if (headers[j].startsWith("$")) { // 隐藏列
continue;
}
cell = row.createCell(colCnt++);
cell.setCellStyle(headStyle);
text = new HSSFRichTextString(headers[j]);
cell.setCellValue(text);
// 设置列宽
sheet.setColumnWidth(j, DEFAULT_COLUMN_WIDTH);
}
}
// 遍历集合数据,产生数据行
int height = data.length;
int width = height > 0 ? data[0].length : 0;
Row row;
Cell cell;
for (int j = 0; j < height; j++) {
row = sheet.createRow(i++);
row.setHeight(DEFAULT_ROW_HEIGHT);
colCnt = 0;
for (int k = 0; k < width; k++) {
if (headers[k].startsWith("$")) { // 隐藏列
continue;
}
cell = row.createCell(colCnt++);
cell.setCellStyle(bodyStyle);
cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue(data[j][k]);
}
}
workbook.write(out);
out.flush();
} catch (IOException e) {
throw e;
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL到指定IO设备
* @param out 与输出设备关联的流对象,可以将EXCEL文档导出到本地文件或者网络中
* @param sheetName sheet表名
* @param headers 标题行(String[])
* @param dataSet 需要显示的数据集合,集合中一定要放置符合javabean风格的类的对象
* @param dateFormat 由yyyy年、MM月、dd日、HH小时、mm分钟、ss秒构成的字符串
* @throws IOException
* @author liyang
*/
private static void export(OutputStream out, String sheetName, String[] headers,
Collection<?> dataSet, String dateFormat) throws IOException {
String[][] data = convertCollectionToStringArray(dataSet, dateFormat);
export(out, sheetName, headers, data);
}
/**
* 导出EXCEL到指定IO设备
* @param out 与输出设备关联的流对象,可以将EXCEL文档导出到本地文件或者网络中
* @param sheetName sheet表名
* @param headers 标题行(String[][]),支持合并单元格
* @param data 数据(String[][] data)
* @throws IOException
* @author liyang
*/
private static void export(OutputStream out, String sheetName, String[][] headers, String[][] data) throws IOException {
HSSFWorkbook workbook = null;
try {
// 声明一个工作薄
workbook = new HSSFWorkbook();
// 生成一个表格
Sheet sheet = workbook.createSheet(sheetName);
// 生成标题样式
CellStyle headStyle = getHeadStyle(workbook);
// 生成记录样式
CellStyle bodyStyle = getBodyStyle(workbook);
List<CellRangeAddress> mergeList = new ArrayList<CellRangeAddress>(); // 合并单元格区域
CellRangeAddress merge;
int rowIndex = 0, colIndex;
Row row;
Cell cell;
String[] line;
String th, span;
int rowspan, colspan;
// 产生表格标题行
for (int i = 0; i < headers.length; i++) {
row = sheet.createRow(rowIndex);
row.setHeight(DEFAULT_ROW_HEIGHT);
line = headers[i];
colIndex = 0;
for (int j = 0; j < line.length; j++) {
th = line[j];
if (th.startsWith("$")) { // 隐藏列
continue;
}
cell = row.createCell(colIndex);
cell.setCellStyle(headStyle);
if (rowIndex == 0) { // 设置列宽
sheet.setColumnWidth(colIndex, DEFAULT_COLUMN_WIDTH);
}
th = th.replaceAll("#", "");
rowspan = 1;
colspan = 1;
if (th.contains("^")) { // 多行
span = "";
if (th.contains("*")) { // 多列
span = th.substring(th.indexOf("^") + 1, th.indexOf("*"));
} else if (th.indexOf("^") < th.length() - 1) {
span = th.substring(th.indexOf("^") + 1);
}
rowspan = Tools.isEmpty(span) ? 1 : Integer.parseInt(span);
if (rowspan > 1) {
merge = new CellRangeAddress(rowIndex, rowIndex + rowspan - 1, colIndex, colIndex);
mergeList.add(merge);
}
}
if (th.contains("*")) { // 多列
colspan = th.indexOf("*") == th.length() - 1 ?
1 : Integer.parseInt(th.substring(th.indexOf("*") + 1));
if (colspan > 1) {
merge = new CellRangeAddress(rowIndex, rowIndex, colIndex, colIndex + colspan - 1);
mergeList.add(merge);
}
}
if (th.contains("^")) {
th = th.substring(0, th.indexOf("^"));
} else if (th.contains("*")) {
th = th.substring(0, th.indexOf("*"));
}
th = Tools.isEmpty(th) || th.contains(" ") ? "" : th;
cell.setCellValue(new HSSFRichTextString(th));
colIndex++;
while (colspan-- > 1) {
cell = row.createCell(colIndex);
cell.setCellStyle(headStyle);
cell.setCellValue("");
if (rowIndex == 0) { // 设置列宽
sheet.setColumnWidth(colIndex, DEFAULT_COLUMN_WIDTH);
}
colIndex++;
}
}
rowIndex++;
}
// 遍历集合数据,产生数据行
for (int i = 0; i < data.length; i++) {
row = sheet.createRow(rowIndex++);
row.setHeight(DEFAULT_ROW_HEIGHT);
colIndex = 0;
for (int j = 0; j < data[i].length; j++) {
if (headers[headers.length - 1][j].startsWith("$")) { // 隐藏列
continue;
}
cell = row.createCell(colIndex++);
cell.setCellStyle(bodyStyle);
cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue(data[i][j]);
}
}
// 查询合并单元列
String old, curr;
int oldIndex, currIndex;
colIndex = 0;
for (int j = 0; j < headers[headers.length - 1].length; j++) {
th = headers[headers.length - 1][j];
if (th.startsWith("$")) { // 隐藏列
continue;
}
if (th.contains("#")) { // 此列值相同的行合并单元格
old = data[0][j];
oldIndex = 0;
curr = "";
currIndex = 0;
for (int i = 1; i < data.length; i++) {
curr = data[i][j];
currIndex = i;
if (!curr.equals(old)) {
if (currIndex - oldIndex > 1) {
merge = new CellRangeAddress(oldIndex + headers.length, currIndex + headers.length - 1, colIndex, colIndex);
mergeList.add(merge);
}
old = curr;
oldIndex = currIndex;
}
}
if (curr.equals(old) && currIndex - oldIndex > 1) {
merge = new CellRangeAddress(oldIndex + headers.length, currIndex + headers.length, colIndex, colIndex);
mergeList.add(merge);
}
}
colIndex++;
}
// 合并单元格
CellRangeAddress[] mergeArray = new CellRangeAddress[mergeList.size()];
for (int i = 0; i < mergeList.size(); i++) {
mergeArray[i] = mergeList.get(i);
}
merge(sheet, mergeArray);
workbook.write(out);
out.flush();
} catch (IOException e) {
throw e;
} finally {
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 导出EXCEL到指定IO设备
* @param out 与输出设备关联的流对象,可以将EXCEL文档导出到本地文件或者网络中
* @param sheetName sheet表名
* @param headers 标题行(String[][]),支持合并单元格
* @param dataSet 需要显示的数据集合,集合中一定要放置符合javabean风格的类的对象
* @param dateFormat 由yyyy年、MM月、dd日、HH小时、mm分钟、ss秒构成的字符串
* @throws IOException
* @author liyang
*/
private static void export(OutputStream out, String sheetName, String[][] headers,
Collection<?> dataSet, String dateFormat) throws IOException {
String[][] data = convertCollectionToStringArray(dataSet, dateFormat);
export(out, sheetName, headers, data);
}
/**
* 获得标题行单元格样式
* @param workbook {@link Workbook}
* @return {@link CellStyle}
* @author liyang
*/
private static CellStyle getHeadStyle(Workbook workbook) {
// 生成标题样式
CellStyle headStyle = workbook.createCellStyle();
// 设置样式
headStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
headStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
headStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
headStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
headStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
headStyle.setFillForegroundColor(HEADER_BACKGROUND_COLOR);
headStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
headStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
// 生成一个字体
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 12);
font.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
font.setColor(HEADER_FONT_COLOR);
headStyle.setFont(font);
return headStyle;
}
/**
* 获得数据行单元格样式
* @param workbook {@link Workbook}
* @return {@link CellStyle}
* @author liyang
*/
private static CellStyle getBodyStyle(Workbook workbook) {
// 生成记录样式
CellStyle bodyStyle = workbook.createCellStyle();
bodyStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
bodyStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
bodyStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
bodyStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
bodyStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
bodyStyle.setFillForegroundColor(HSSFColor.WHITE.index);
bodyStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
bodyStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
bodyStyle.setWrapText(true);
// 生成字体
Font font2 = workbook.createFont();
font2.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
// 把字体应用到当前的样式
bodyStyle.setFont(font2);
return bodyStyle;
}
/**
* 把对象集合转成字符串二维数组
* @param dataSet 对象集合
* @param dateFormat 由yyyy年、MM月、dd日、HH小时、mm分钟、ss秒构成的字符串
* @return {String[][]}
* @author liyang
*/
private static String[][] convertCollectionToStringArray(Collection<?> dataSet, String dateFormat) {
// 将Collection<?>集合转成String[][]二维数组
List<String[]> lineList = new ArrayList<String[]>();
String[] line;
int colCnt = 0;
Iterator<?> it = dataSet.iterator();
Object obj, value;
Field[] fields;
String[] fieldNames;
Class<?>[] fieldTypes;
Field field;
String fieldName, methodName, type;
Class<?> objClass;
Method getMethod;
if (dateFormat == null || dateFormat.trim() == "") {
dateFormat = "yyyy-MM-dd";
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
while (it.hasNext()) {
obj = it.next();
if (obj == null) {
continue;
}
// 利用反射,根据javabean属性的先后顺序,动态调用getXxx()方法得到属性值
fields = obj.getClass().getDeclaredFields();
fieldNames = new String[fields.length];
fieldTypes = new Class<?>[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldNames[i] = fields[i].getName();
fieldTypes[i] = fields[i].getType();
}
if (lineList.size() == 0) {
colCnt = fields.length;
} else if (colCnt != fields.length) {
throw new RuntimeException(COLUMN_COUNT_NOT_SAME);
}
line = new String[colCnt];
for (int i = 0; i < fields.length; i++) {
field = fields[i];
fieldName = field.getName();
methodName = "get"
+ fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
value = null;
objClass = obj.getClass();
try {
getMethod = objClass.getMethod(methodName);
value = getMethod.invoke(obj);
} catch (Exception e) {
LOGGER.error("获取属性值失败", e);
}
if (value == null) {
line[i] = "";
continue;
}
// 判断值的类型后进行强制类型转换
type = fieldTypes[i].getName();
if ("char".equals(type) || "java.lang.String".equals(type)) {
line[i] = (String)value;
} else if ("short".equals(type)
|| "int".equals(type)
|| "long".equals(type)
|| "float".equals(type)
|| "double".equals(type)
|| value instanceof java.lang.Number) {
line[i] = String.valueOf(value);
} else if ("boolean".equals(type)
|| "java.lang.Boolean".equals(type)) {
line[i] = (Boolean)value ? "是" : "否";
} else if ("java.util.Date".equals(type)) {
line[i] = sdf.format((Date)value);
}
}
lineList.add(line);
}
String[][] data = new String[lineList.size()][colCnt];
for (int i = 0; i < lineList.size(); i++) {
data[i] = lineList.get(i);
}
return data;
}
/**
* 读取EXCEL文件获得String[][]数组
* @param workbook {@link Workbook}对象
* @param firstRowIndex 起始行索引,从0开始
* @param lastRowIndex 结束行索引,从0开始
* @param firstColIndex 起始列索引,从0开始
* @param lastColIndex 结束列索引,从0开始
* @return
* @author liyang
*/
private static String[][] xlsToStringArray(Workbook workbook, int firstRowIndex, int lastRowIndex, int firstColIndex, int lastColIndex) {
String[][] array = new String[lastRowIndex - firstRowIndex + 1][lastColIndex - firstColIndex + 1];
Sheet sheet = workbook.getSheetAt(0);
Row row;
Cell cell;
for (int rowIndex = firstRowIndex; rowIndex <= lastRowIndex; rowIndex++) {
row = sheet.getRow(rowIndex);
for (int colIndex = firstColIndex; colIndex <= lastColIndex; colIndex++) {
cell = row.getCell(colIndex);
array[rowIndex - firstRowIndex][colIndex - firstColIndex] = cell.getStringCellValue();
}
}
return array;
}
/**
* 重置response以下载
* @param response {@link HttpServletResponse}
* @param filename 文件名(不含后缀)
* @param encoding 字符编码
* @throws UnsupportedEncodingException
* @author liyang
*/
private static void resetResponseToDownload(HttpServletResponse response, String filename, String encoding)
throws UnsupportedEncodingException {
filename = new String((filename + ".xls").getBytes(encoding), "iso8859-1");
response.setContentType("application/vnd.ms-excel;charset=iso8859-1");
response.setHeader("Content-disposition", "attachment; filename=" + filename);
}
/**
* 创建文件
* @param filename 文件路径
* @throws IOException
* @author liyang
*/
private static File createFile(String filename) throws IOException {
File file = new File(filename);
File dir = new File(file.getParent());
if (!dir.exists()) {
dir.mkdirs();
}
if (!file.exists()) {
file.createNewFile();
}
return file;
}
}