package com.ruoyi.common.utils.poi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.poi.hssf.usermodel.DVConstraint;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.config.RuoYiConfig;
import com.ruoyi.framework.web.domain.AjaxResult;/**
* Excel相关处理
*
* @author ruoyi*/
public class ExcelUtil{private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);public Classclazz;public ExcelUtil(Classclazz)
{this.clazz =clazz;
}/**
* 对excel表单默认第一个索引名转换成list
*
* @param input 输入流
* @return 转换后集合*/
public ListimportExcel(InputStream input) throws Exception
{returnimportExcel(StringUtils.EMPTY, input);
}/**
* 对excel表单指定表格索引名转换成list
*
* @param sheetName 表格索引名
* @param input 输入流
* @return 转换后集合*/
public ListimportExcel(String sheetName, InputStream input) throws Exception
{
List list = new ArrayList();
Workbook workbook=WorkbookFactory.create(input);
Sheet sheet= null;if(StringUtils.isNotEmpty(sheetName))
{//如果指定sheet名,则取指定sheet中的内容.
sheet =workbook.getSheet(sheetName);
}else{//如果传入的sheet名不存在则默认指向第1个sheet.
sheet = workbook.getSheetAt(0);
}if (sheet == null)
{throw new IOException("文件sheet不存在");
}int rows =sheet.getPhysicalNumberOfRows();if (rows > 0)
{//默认序号
int serialNum = 0;//有数据时才处理 得到类的所有field.
Field[] allFields =clazz.getDeclaredFields();//定义一个map用于存放列的序号和field.
Map fieldsMap = new HashMap();for (int col = 0; col < allFields.length; col++)
{
Field field=allFields[col];//将有注解的field存放到map中.
if (field.isAnnotationPresent(Excel.class))
{//设置类的私有字段属性可访问.
field.setAccessible(true);
fieldsMap.put(++serialNum, field);
}
}for (int i = 1; i < rows; i++)
{//从第2行开始取数据,默认第一行是表头.
Row row =sheet.getRow(i);int cellNum =serialNum;
T entity= null;for (int j = 0; j < cellNum; j++)
{
Cell cell=row.getCell(j);if (cell == null)
{continue;
}else{//先设置Cell的类型,然后就可以把纯数字作为String类型读进来了
row.getCell(j).setCellType(CellType.STRING);
cell=row.getCell(j);
}
String c=cell.getStringCellValue();if(StringUtils.isEmpty(c))
{continue;
}//如果不存在实例则新建.
entity = (entity == null ?clazz.newInstance() : entity);//从map中得到对应列的field.
Field field = fieldsMap.get(j + 1);//取得类型,并根据对象类型设置值.
Class> fieldType =field.getType();if (String.class ==fieldType)
{
field.set(entity, String.valueOf(c));
}else if ((Integer.TYPE == fieldType) || (Integer.class ==fieldType))
{
field.set(entity, Integer.parseInt(c));
}else if ((Long.TYPE == fieldType) || (Long.class ==fieldType))
{
field.set(entity, Long.valueOf(c));
}else if ((Float.TYPE == fieldType) || (Float.class ==fieldType))
{
field.set(entity, Float.valueOf(c));
}else if ((Short.TYPE == fieldType) || (Short.class ==fieldType))
{
field.set(entity, Short.valueOf(c));
}else if ((Double.TYPE == fieldType) || (Double.class ==fieldType))
{
field.set(entity, Double.valueOf(c));
}else if (Character.TYPE ==fieldType)
{if ((c != null) && (c.length() > 0))
{
field.set(entity, Character.valueOf(c.charAt(0)));
}
}else if (java.util.Date.class ==fieldType)
{if (cell.getCellTypeEnum() ==CellType.NUMERIC)
{
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
cell.setCellValue(sdf.format(cell.getNumericCellValue()));
c=sdf.format(cell.getNumericCellValue());
}else{
c=cell.getStringCellValue();
}
}else if (java.math.BigDecimal.class ==fieldType)
{
c=cell.getStringCellValue();
}
}if (entity != null)
{
list.add(entity);
}
}
}returnlist;
}/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @return 结果*/
public AjaxResult exportExcel(Listlist, String sheetName)
{
OutputStreamout = null;
HSSFWorkbook workbook= null;try{//得到所有定义字段
Field[] allFields =clazz.getDeclaredFields();
List fields = new ArrayList();//得到所有field并存放到一个list中.
for(Field field : allFields)
{if (field.isAnnotationPresent(Excel.class))
{
fields.add(field);
}
}//产生工作薄对象
workbook = newHSSFWorkbook();//excel2003中每个sheet中最多有65536行
int sheetSize = 65536;//取出一共有多少个sheet.
double sheetNo = Math.ceil(list.size() /sheetSize);for (int index = 0; index <= sheetNo; index++)
{//产生工作表对象
HSSFSheet sheet =workbook.createSheet();if (sheetNo == 0)
{
workbook.setSheetName(index, sheetName);
}else{//设置工作表的名称.
workbook.setSheetName(index, sheetName +index);
}
HSSFRow row;
HSSFCell cell;//产生单元格//产生一行
row = sheet.createRow(0);//写入各个字段的列头名称
for (int i = 0; i < fields.size(); i++)
{
Field field= fields.get(i);
Excel attr= field.getAnnotation(Excel.class);//创建列
cell =row.createCell(i);//设置列中写入内容为String类型
cell.setCellType(CellType.STRING);
HSSFCellStyle cellStyle=workbook.createCellStyle();
cellStyle.setAlignment(HorizontalAlignment.CENTER);
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);if (attr.name().indexOf("注:") >= 0)
{
HSSFFont font=workbook.createFont();
font.setColor(HSSFFont.COLOR_RED);
cellStyle.setFont(font);
cellStyle.setFillForegroundColor(HSSFColorPredefined.YELLOW.getIndex());
sheet.setColumnWidth(i,6000);
}else{
HSSFFont font=workbook.createFont();//粗体显示
font.setBold(true);//选择需要用到的字体格式
cellStyle.setFont(font);
cellStyle.setFillForegroundColor(HSSFColorPredefined.LIGHT_YELLOW.getIndex());//设置列宽
sheet.setColumnWidth(i, (int) ((attr.width() + 0.72) * 256));
row.setHeight((short) (attr.height() * 20));
}
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cellStyle.setWrapText(true);
cell.setCellStyle(cellStyle);//写入列名
cell.setCellValue(attr.name());//如果设置了提示信息则鼠标放上去提示.
if(StringUtils.isNotEmpty(attr.prompt()))
{//这里默认设了2-101列提示.
setHSSFPrompt(sheet, "", attr.prompt(), 1, 100, i, i);
}//如果设置了combo属性则本列只能选择不能输入
if (attr.combo().length > 0)
{//这里默认设了2-101列只能选择不能输入.
setHSSFValidation(sheet, attr.combo(), 1, 100, i, i);
}
}int startNo = index *sheetSize;int endNo = Math.min(startNo +sheetSize, list.size());//写入各条记录,每条记录对应excel表中的一行
HSSFCellStyle cs =workbook.createCellStyle();
cs.setAlignment(HorizontalAlignment.CENTER);
cs.setVerticalAlignment(VerticalAlignment.CENTER);for (int i = startNo; i < endNo; i++)
{
row= sheet.createRow(i + 1 -startNo);//得到导出对象.
T vo = (T) list.get(i);for (int j = 0; j < fields.size(); j++)
{//获得field.
Field field = fields.get(j);//设置实体类私有属性可访问
field.setAccessible(true);
Excel attr= field.getAnnotation(Excel.class);try{//设置行高
row.setHeight((short) (attr.height() * 20));//根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
if(attr.isExport())
{//创建cell
cell =row.createCell(j);
cell.setCellStyle(cs);if (vo == null)
{//如果数据存在就填入,不存在填入空格.
cell.setCellValue("");continue;
}
String dateFormat=attr.dateFormat();
String readConverterExp=attr.readConverterExp();if(StringUtils.isNotEmpty(dateFormat))
{
cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) field.get(vo)));
}else if(StringUtils.isNotEmpty(readConverterExp))
{
cell.setCellValue(convertByExp(String.valueOf(field.get(vo)), readConverterExp));
}else{
cell.setCellType(CellType.STRING);//如果数据存在就填入,不存在填入空格.
cell.setCellValue(StringUtils.isNull(field.get(vo)) ? attr.defaultValue() : field.get(vo) +attr.suffix());
}
}
}catch(Exception e)
{
log.error("导出Excel失败{}", e.getMessage());
}
}
}
}
String filename=encodingFilename(sheetName);out = newFileOutputStream(getAbsoluteFile(filename));
workbook.write(out);returnAjaxResult.success(filename);
}catch(Exception e)
{
log.error("导出Excel异常{}", e.getMessage());return AjaxResult.error("导出Excel失败,请联系网站管理员!");
}finally{if (workbook != null)
{try{
workbook.close();
}catch(IOException e1)
{
e1.printStackTrace();
}
}if (out != null)
{try{out.close();
}catch(IOException e1)
{
e1.printStackTrace();
}
}
}
}/**
* 设置单元格上提示
*
* @param sheet 要设置的sheet.
* @param promptTitle 标题
* @param promptContent 内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
* @return 设置好的sheet.*/
public static HSSFSheet setHSSFPrompt(HSSFSheet sheet, String promptTitle, String promptContent, intfirstRow,int endRow, int firstCol, intendCol)
{//构造constraint对象
DVConstraint constraint = DVConstraint.createCustomFormulaConstraint("DD1");//四个参数分别是:起始行、终止行、起始列、终止列
CellRangeAddressList regions = newCellRangeAddressList(firstRow, endRow, firstCol, endCol);//数据有效性对象
HSSFDataValidation dataValidationView = newHSSFDataValidation(regions, constraint);
dataValidationView.createPromptBox(promptTitle, promptContent);
sheet.addValidationData(dataValidationView);returnsheet;
}/**
* 设置某些列的值只能输入预制的数据,显示下拉框.
*
* @param sheet 要设置的sheet.
* @param textlist 下拉框显示的内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
* @return 设置好的sheet.*/
public static HSSFSheet setHSSFValidation(HSSFSheet sheet, String[] textlist, int firstRow, intendRow,int firstCol, intendCol)
{//加载下拉列表内容
DVConstraint constraint =DVConstraint.createExplicitListConstraint(textlist);//设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
CellRangeAddressList regions = newCellRangeAddressList(firstRow, endRow, firstCol, endCol);//数据有效性对象
HSSFDataValidation dataValidationList = newHSSFDataValidation(regions, constraint);
sheet.addValidationData(dataValidationList);returnsheet;
}/**
* 解析导出值 0=男,1=女,2=未知
*
* @param propertyValue 参数值
* @param converterExp 翻译注解
* @return 解析后值
* @throws Exception*/
public staticString convertByExp(String propertyValue, String converterExp) throws Exception
{try{
String[] convertSource= converterExp.split(",");for(String item : convertSource)
{
String[] itemArray= item.split("=");if (itemArray[0].equals(propertyValue))
{return itemArray[1];
}
}
}catch(Exception e)
{throwe;
}returnpropertyValue;
}/**
* 编码文件名*/
publicString encodingFilename(String filename)
{
filename= UUID.randomUUID().toString() + "_" + filename + ".xls";returnfilename;
}/**
* 获取下载路径
*
* @param filename 文件名称*/
publicString getAbsoluteFile(String filename)
{
String downloadPath= RuoYiConfig.getDownloadPath() +filename;
File desc= newFile(downloadPath);if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}returndownloadPath;
}
}