日常开发工作中对于文件的相关操作大家多少都会涉及:上传解析、数据导出等。此篇内容主要分享一下工作中常用的Excel文件的解析和导出工作类实现。
实践
1.maven依赖
首先引入POI包依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
2.API
大家需要了解一下相关的API类,便于后面理解代码逻辑和排查问题,重写相关逻辑实现自己的业务需求。
1.import org.apache.poi.ss.usermodel.Workbook,对应Excel文档;
2.import org.apache.poi.hssf.usermodel.HSSFWorkbook,对应xls格式的Excel文档;
3.import org.apache.poi.xssf.usermodel.XSSFWorkbook,对应xlsx格式的Excel文档;
4.import org.apache.poi.ss.usermodel.Sheet,对应Excel文档中的一个sheet;
5.import org.apache.poi.ss.usermodel.Row,对应一个sheet中的一行;
6.import org.apache.poi.ss.usermodel.Cell,对应一个单元格。
3.导入
public class ImportExcelUtil {
private static final String EXCEL_XLS_SUFFIX = ".xls";
private static final String EXCEL_XLSX_SUFFIX = ".xlsx";
public static List<Map<Integer, Object>> readExcelContent(String filepath, Integer rowIndex, Integer columnIndex) throws Exception {
List<Map<Integer, Object>> returnList = new LinkedList<>();
Workbook wb = null;
Sheet sheet;
Row row;
try {
InputStream is = new FileInputStream(filepath);
if (filepath.endsWith(EXCEL_XLS_SUFFIX)) {
wb = new HSSFWorkbook(is);
} else if (filepath.endsWith(EXCEL_XLSX_SUFFIX)) {
wb = new XSSFWorkbook(is);
}
if (wb == null) {
throw new Exception("Workbook对象为空!");
}
sheet = wb.getSheetAt(0);
//解析文件总行数、总列数
int rowNum = rowIndex != null ? rowIndex : sheet.getLastRowNum();
row = sheet.getRow(0);
int colNum = columnIndex != null ? columnIndex : row.getLastCellNum();
//循环列
for (int colIndex = colNum; colIndex > 0; colIndex--) {
Cell cell = row.getCell(colIndex);
if (cell != null && !"".equals(cell.toString())) {
colNum = colIndex;
break;
}
}
logger.info("have data col:{}", colNum);
// 正文内容应该从第二行开始,第一行为表头的标题
for (int i = 0; i <= rowNum; i++) {
row = sheet.getRow(i);
int j = 0;
int size = (int) (colNum / .75f) + 1;
//存储单元格数据
Map<Integer, Object> cellValue = new LinkedHashMap<>(size);
if (row == null) {
continue;
}
while (j <= colNum) {
Cell cell = row.getCell(j);
String value = "";
//日期单元格需格式化日期
if (cell != null) {
if (cell.getCellType() == CellType.NUMERIC) {
if (HSSFDateUtil.isCellDateFormatted(cell)) {
Date d = cell.getDateCellValue();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
value = formatter.format(d);
} else if (cell.toString().contains("E")) {
DecimalFormat nf = new DecimalFormat("0");
value = nf.format(cell.getNumericCellValue());
} else {
value = cell.toString().endsWith(".0") ? cell.toString().replace(".0", "") : cell.toString().trim();
}
} else if (cell.getCellType() == CellType.FORMULA) {
value = String.valueOf(cell.getNumericCellValue());
} else {
value = cell.toString().trim();
}
}
cellValue.put(j, value);
j++;
}
returnList.add(cellValueMap);
}
wb.close();
} catch (FileNotFoundException e) {
logger.error("FileNotFoundException", e);
} catch (IOException e) {
logger.error("IOException", e);
} finally {
if (wb != null) {
wb.close();
}
}
return returnList;
}
}
** 解释: **
此处方法只是提供一个思路,后期大家可以根据自己的业务需求改写,比如指定sheet,行号,列号等等。着重说明一下返回值数据结构List,主要存储结构为Map,key=列号,value=单元格内容;这种操作便于后期验证必须列是否缺失、以及可以动态设计上传文件的列结构不必固定列位置等等。
4.导出
public class ExportExcelUtil {
public static<T> HSSFWorkbook exportExcel(String sheetName, String[] excelTitle, Collection<T> dataCollection) {
//创建一个Excel文件
HSSFWorkbook workbook = new HSSFWorkbook();
//创建一个Sheet表格工作空间
HSSFSheet sheet = workbook.createSheet(sheetName);
HSSFCellStyle style = workbook.createCellStyle();
//设置表格默认宽度
sheet.setDefaultColumnWidth(20);
//设置表格的表头
HSSFCell cellHeader;
HSSFRow row = sheet.createRow(0);
for (int i = 0; i < excelTitle.length; i++) {
//创建单元格表头
cellHeader = row.createCell(i);
cellHeader.setCellValue(new HSSFRichTextString(excelTitle[i]));
}
//匹配表头设置单元格的值
setWorkBookValue(sheet, dataCollection,0, style);
return workbook;
}
@SuppressWarnings("unchecked")
private static<T> void setWorkBookValue(HSSFSheet sheet,Collection<T> dataCollection, int index,HSSFCellStyle style){
T t;
Object[] fields;
String fieldName;
String getMethodName;
HSSFCell cell;
HSSFRow row;
Class tClass;
Method getMethod;
Object value;
//遍历集合设置单元格值
Iterator<T> it = dataCollection.iterator();
while(it.hasNext()){
//创建一行单元格
index ++;
row = sheet.createRow(index);
//获取数据
t = it.next();
//利用反射,根据JavaBean属性的先后顺序,动态调用getXxx()方法得到属性值
fields = t.getClass().getDeclaredFields();
for(int i = 0; i < fields.length; i++){
cell = row.createCell(i);
style.setAlignment(HorizontalAlignment.LEFT);
cell.setCellStyle(style);
//利用反射,根据JavaBean属性的先后顺序,动态调用getXxx()方法得到属性值
Field[] newFields = t.getClass().getDeclaredFields();
fieldName = newFields[i].getName();
getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
try {
tClass = t.getClass();
getMethod = tClass.getMethod(getMethodName, new Class[]{ });
value = getMethod.invoke(t, new Object[]{ });
setCellValue(value,cell);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static void setCellValue(Object value,HSSFCell cell){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String textValue = null;
Pattern pattern = Pattern.compile(RULE);
Matcher matcher;
HSSFRichTextString richTextString;
if (!StringUtils.isEmpty(value)){
//value进行类型转换
if (value instanceof Integer) {
cell.setCellValue((Integer) value);
} else if (value instanceof Float) {
textValue = String.valueOf(value);
cell.setCellValue(textValue);
} else if (value instanceof Double) {
textValue = String.valueOf(value);
cell.setCellValue(textValue);
} else if (value instanceof Long) {
cell.setCellValue((Long) value);
} else if (value instanceof Boolean) {
textValue = "是";
if (!(Boolean) value) {
textValue = "否";
}
} else if (value instanceof Date) {
textValue = sdf.format((Date) value);
} else {
// 其它数据类型都当作字符串简单处理
textValue = value.toString();
}
if (textValue != null) {
matcher = pattern.matcher(textValue);
if (matcher.matches()) {
// 是数字当作double处理
cell.setCellValue(Double.parseDouble(textValue));
} else {
richTextString = new HSSFRichTextString(textValue);
cell.setCellValue(richTextString);
}
}
}
}
public static void exportExcelFile(HttpServletResponse response, HSSFWorkbook workbook, String fileName) throws IOException {
if (workbook != null) {
response.reset();
//指定下载的文件名
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String filePrefix = sdf.format(new Date());
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
fileName = URLEncoder.encode(filePrefix + "_" + fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
BufferedOutputStream bufferedOutput = null;
try {
bufferedOutput = new BufferedOutputStream(response.getOutputStream());
workbook.write(bufferedOutput);
bufferedOutput.flush();
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
if (bufferedOutput != null) {
try {
bufferedOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
外部访问导出文件示例:
public void exportFile(String objectJson, HttpServletResponse response) throws Exception {
....
//省略业务代码
HSSFWorkbook workbook = ExportExcelUtil.exportExcel(SHEET_NAME, TITLE_LINE, xxxList);
ExportExcelUtil.exportExcelFile(response, workbook, FILE_NAME);
}
特殊说明:导出操作实际上利用反射机制实现,则需保证Excel标题列顺序需与导出定义的实体对象属性顺序保持一致,否则会出现错位现象。