使用读取excel工具类的例子,例子中,使用简单,对比灵活。代码简洁。
说明:这块代码是网上别人公开的,具体来源找不到了。我把它做了修改,抽离固定的实体,改成泛型,这样更加灵活。如果侵权,请通知博主。
一.编写工具类
工具类代码:
package com.system.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
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.xssf.usermodel.XSSFWorkbook;
import freemarker.log.Logger;
/**
* Author: laijieuan
* Date: 2020-07-07
* Description: 读取Excel内容
* T 泛型是接收每行数据的类名
*/
public abstract class ExcelReader<T> {
private static Logger logger = Logger.getLogger(ExcelReader.class.getName()); // 日志打印类
private static final String XLS = "xls";
private static final String XLSX = "xlsx";
/**
* 根据文件后缀名类型获取对应的工作簿对象
* @param inputStream 读取文件的输入流
* @param fileType 文件后缀名类型(xls或xlsx)
* @return 包含文件数据的工作簿对象
* @throws IOException
*/
public Workbook getWorkbook(InputStream inputStream, String fileName) throws IOException {
Workbook workbook = null;
if (fileName.endsWith(XLS)) {
workbook = new HSSFWorkbook(inputStream);
} else if (fileName.endsWith(XLSX)) {
workbook = new XSSFWorkbook(inputStream);
}
return workbook;
}
/**
* 读取Excel文件内容
* @param fileName 要读取的Excel文件所在路径
* @return 读取结果列表,读取失败时返回null
*/
public List<T> readExcel(String fileName) {
Workbook workbook = null;
FileInputStream inputStream = null;
try {
// 获取Excel后缀名
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
// 获取Excel文件
File excelFile = new File(fileName);
if (!excelFile.exists()) {
logger.warn("指定的Excel文件不存在!");
return null;
}
// 获取Excel工作簿
inputStream = new FileInputStream(excelFile);
workbook = getWorkbook(inputStream, fileType);
// 读取excel中的数据
List<T> resultDataList = parseExcel(workbook);
return resultDataList;
} catch (Exception e) {
logger.warn("解析Excel失败,文件名:" + fileName + " 错误信息:" + e.getMessage());
return null;
} finally {
try {
if (null != inputStream) {
inputStream.close();
}
} catch (Exception e) {
logger.warn("关闭数据流出错!错误信息:" + e.getMessage());
return null;
}
}
}
/**
* 解析Excel数据
* @param workbook Excel工作簿对象
* @return 解析结果
*/
public List<T> parseExcel(Workbook workbook) {
List<T> resultDataList = new ArrayList<>();
// 解析sheet
for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
Sheet sheet = workbook.getSheetAt(sheetNum);
// 校验sheet是否合法
if (sheet == null) {
continue;
}
// 获取第一行数据
int firstRowNum = sheet.getFirstRowNum();
Row firstRow = sheet.getRow(firstRowNum);
if (null == firstRow) {
logger.warn("解析Excel失败,在第一行没有读取到任何数据!");
}
// 解析每一行的数据,构造数据对象
int rowStart = firstRowNum + 1;
int rowEnd = sheet.getPhysicalNumberOfRows();
for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) {
Row row = sheet.getRow(rowNum);
if (null == row) {
continue;
}
//读取当前行的数据,该方法需要重写
T resultData = convertRowToData(row);
if (null == resultData) {
logger.warn("第 " + row.getRowNum() + "行数据不合法,已忽略!");
continue;
}
resultDataList.add(resultData);
}
}
return resultDataList;
}
/**
* 将单元格内容转换为字符串
* @param cell
* @return
*/
public static String convertCellValueToString(Cell cell) {
if(cell==null){
return null;
}
String returnValue = null;
switch (cell.getCellType()) {
case Cell.CELL_TYPE_NUMERIC: //数字
Double doubleValue = cell.getNumericCellValue();
// 格式化科学计数法,取一位整数
DecimalFormat df = new DecimalFormat("0");
returnValue = df.format(doubleValue);
break;
case Cell.CELL_TYPE_STRING: //字符串
returnValue = cell.getStringCellValue();
break;
case Cell.CELL_TYPE_BOOLEAN: //布尔
Boolean booleanValue = cell.getBooleanCellValue();
returnValue = booleanValue.toString();
break;
case Cell.CELL_TYPE_BLANK: // 空值
break;
case Cell.CELL_TYPE_FORMULA: // 公式
returnValue = cell.getCellFormula();
break;
case Cell.CELL_TYPE_ERROR: // 故障
break;
default:
break;
}
return returnValue;
}
/**
* 为了灵活使用该工具类,抽取泛型来完成
* 提取每一行中需要的数据,构造成为一个结果数据对象
* 当该行中有单元格的数据为空或不合法时,忽略该行的数据
*
* @param row 行数据
* @return 解析后的行数据对象,行数据错误时返回null
*/
public abstract T convertRowToData(Row row);
}
工具类就此写完了
二.例子说明
现在有一个类的实体是这样的:
package com.wl.differentCountries.entity;
/**
*国别类
*/
public class Country {
/**
* 国别代码
*/
private String countrycode;
/**
* 国别中文简称
*/
private String countryname;
/**
* 国别英文简称
*/
private String countryenname;
/**
* 国别英文缩写
*/
private String countryenshort;
/**
* 优普税率
*/
private String applytaxtype;
/**
* 国别描述
*/
private String countrydesc;
/**
* 备注
*/
private String comments;
/**
* 状态
*/
private String states;
/**
* 最终目的国别代码
*/
private String lastcountrycode;
public String getCountrycode() {
return countrycode;
}
public void setCountrycode(String countrycode) {
this.countrycode = countrycode == null ? null : countrycode.trim();
}
public String getCountryname() {
return countryname;
}
public void setCountryname(String countryname) {
this.countryname = countryname == null ? null : countryname.trim();
}
public String getCountryenname() {
return countryenname;
}
public void setCountryenname(String countryenname) {
this.countryenname = countryenname == null ? null : countryenname.trim();
}
public String getCountryenshort() {
return countryenshort;
}
public void setCountryenshort(String countryenshort) {
this.countryenshort = countryenshort == null ? null : countryenshort.trim();
}
public String getApplytaxtype() {
return applytaxtype;
}
public void setApplytaxtype(String applytaxtype) {
this.applytaxtype = applytaxtype == null ? null : applytaxtype.trim();
}
public String getCountrydesc() {
return countrydesc;
}
public void setCountrydesc(String countrydesc) {
this.countrydesc = countrydesc == null ? null : countrydesc.trim();
}
public String getComments() {
return comments;
}
public void setComments(String comments) {
this.comments = comments == null ? null : comments.trim();
}
public String getStates() {
return states;
}
public void setStates(String states) {
this.states = states == null ? null : states.trim();
}
public String getLastcountrycode() {
return lastcountrycode;
}
public void setLastcountrycode(String lastcountrycode) {
this.lastcountrycode = lastcountrycode == null ? null : lastcountrycode.trim();
}
@Override
public String toString() {
return "Country [countrycode=" + countrycode + ", countryname=" + countryname + ", countryenname="
+ countryenname + ", countryenshort=" + countryenshort + ", applytaxtype=" + applytaxtype
+ ", countrydesc=" + countrydesc + ", comments=" + comments + ", states=" + states
+ ", lastcountrycode=" + lastcountrycode + "]";
}
}
然后这个实体的数据基本都是由公司的人员从excel里的填写好后,上传到系统,系统读取excel并封装成的
excel模板:
国别代码 | 国别中文简称 | 国别英文简称 | 国别英文缩写 | 优普税率 | 国别描述 | 备注 | 状态 | 最终目的国别代码 |
502 | 美国 | XX | UA | XXXX | XXX | XX | Y | XXX |
第一步:继承excelReader工具类并重写读取数据方法。
package com.wl.differentCountries.utils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import com.system.util.ExcelReader;
import com.wl.differentCountries.entity.Country;
/**
* 针对这个国别类封装一个工具
* 必须继承ExcelReader<T>
* T是自由的,当然,既然针对的是国别类,这里就是写国别类
*/
public class CountryExcelReader extends ExcelReader<Country>{
/*重写方法,读取每一行数据并封装成实体*/
@Override
public Country convertRowToData(Row row) {
//先new一个国别实体
Country resultData = new Country();
//
Cell cell;
//当前列号,默认值0
int cellNum = 0;
//获取最大列号,
row.getPhysicalNumberOfCells();
//通过行获取列 获取第一列
cell = row.getCell(cellNum++);
// 获取国别代码 读取数据并装成String
String countryCode = convertCellValueToString(cell);
//封装至实体属性
resultData.setCountrycode(countryCode);
//通过行获取列 获取第二列 以下就以此类推,有多少列就读多少次,也可以通过最大的列数号遍历,通过反射完成封装,这里就不弄这么复杂的了。
cell = row.getCell(cellNum++);
// 获取国别中文简称
String countryname = convertCellValueToString(cell);
resultData.setCountryname(countryname);
// 获取国别英文简称
cell = row.getCell(cellNum++);
String countryenname = convertCellValueToString(cell);
resultData.setCountryenname(countryenname);
// 获取国别英文缩写
cell = row.getCell(cellNum++);
String countryenshort = convertCellValueToString(cell);
resultData.setCountryenshort(countryenshort);
// 获取优普税率
cell = row.getCell(cellNum++);
String applytaxtype = convertCellValueToString(cell);
resultData.setApplytaxtype(applytaxtype);
// 获取国别描述
cell = row.getCell(cellNum++);
String countrydesc = convertCellValueToString(cell);
resultData.setCountrydesc(countrydesc);
// 获取备注
cell = row.getCell(cellNum++);
String comments = convertCellValueToString(cell);
resultData.setComments(comments);
// 获取状态
cell = row.getCell(cellNum++);
String states = convertCellValueToString(cell);
resultData.setComments(comments);
// 获取最终目的国别代码
cell = row.getCell(cellNum++);
String lastcountrycode = convertCellValueToString(cell);
resultData.setLastcountrycode(lastcountrycode);
return resultData;
}
}
Controller层:获取上传的excel文件
package com.wl.differentCountries.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.system.util.ResultUtils;
import com.system.vo.Result;
import com.wl.differentCountries.entity.Country;
import com.wl.differentCountries.service.WLCountryService;
/**
*
* @author laijieguan
* 2020-07-10
*/
@RestController
@RequestMapping("country")
public class CountryController {
@Autowired
private WLCountryService wlCountryService;
/**
* 根据国别代码来查询
*/
@RequestMapping("selByCountryCode")
private Result<Object> selByCountryCode(String countryCode){
System.out.println(countryCode);
Country country = wlCountryService.selectByPrimaryKey(countryCode);
if(country != null) {
return ResultUtils.data(country);
}
return ResultUtils.error("未查询到相关国别信息!");
}
/**
*获取上传的文件
*/
@RequestMapping("importCountryData")
private Result<Object> importCountryData(@RequestParam(value = "file", required = false) MultipartFile mFile) {
//这个地方应该对该文件的格式做判断,如果不是excel的话返回提示
String type = "";
try {
Boolean importAddr = wlCountryService.importAddr(mFile, type);
if (importAddr) {
return ResultUtils.success("导入成功");
} else {
return ResultUtils.error("导入失败");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return ResultUtils.error("导入失败");
}
}
}
Service层:读取数据,并对数据进行业务处理
package com.wl.differentCountries.service;
import java.io.InputStream;
import java.util.List;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import com.wl.differentCountries.dao.WLCountryMapper;
import com.wl.differentCountries.entity.Country;
import com.wl.differentCountries.utils.CountryExcelReader;
@Service
@Transactional
public class WLCountryServiceImpl implements WLCountryService{
@Autowired
private WLCountryMapper wlCountryMapper;
@Override
public Integer insert(Country country) {
return wlCountryMapper.insert(country);
}
@Override
public Country selectByPrimaryKey(String countryCode) {
return wlCountryMapper.selectByPrimaryKey(countryCode);
}
@Override
public Integer updateByPrimaryKeySelective(Country country) {
return wlCountryMapper.updateByPrimaryKeySelective(country);
}
/**
*读取excel数据
*/
@Override
public Boolean importAddr(MultipartFile mFile, String type) throws Exception {
//获取流
InputStream inputStream = mFile.getInputStream();
//获取文件名
String fileName = mFile.getOriginalFilename();
//new 一个读取对应实体的excel读解器
CountryExcelReader countryExcelReader = new CountryExcelReader();
//通过流来获取workbook实体
Workbook wb = countryExcelReader.getWorkbook(inputStream, fileName);
//解析excel数据,封装后返回List集合
List<Country> parseExcel = countryExcelReader.parseExcel(wb);
int count = 0;
//遍历每一实例,做对应的业务操作。
for(int i = 0 ;i< parseExcel.size();i++) {
System.out.println(parseExcel.get(i));
}
if(count == parseExcel.size()) { //这个是我的业务操作需要的,业务代码已经删除各位根据自己情况完成即可
return true;
}
return false;
}
在读取每列数据的时候,其实是不太灵活的,可以做成代理模式。具体的我就不详细说了,因为这块代码实现属于机密。哈哈哈哈哈。