使用poi导入excel详细代码
1.导入依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.core</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId>
<version>1.0.6</version>
</dependency>
版本自己选择即可.
2.编写导入工具类
具体代码如下
/**
* 导入excel表格.返回的是List<Object> 的数据结构,使用反射去赋值.
* @param file 导入的文件
* @param clazz 存储数据的实体
* @param <T> 表示这个方法是泛型方法.
* @return
*/
public static <T> List<T> readExcel(MultipartFile file, Class<T> clazz){
try{
//最终返回数据
List<T> resultList = new ArrayList<T>();
Workbook workbook = null;
InputStream is = file.getInputStream();
String name = file.getOriginalFilename().toLowerCase();
// 创建excel操作对象
if (name.contains(".xlsx") || name.contains(".xls")) {
//使用工厂方法创建.
workbook = WorkbookFactory.create(is);
}
//得到一个工作表
Sheet sheet = workbook.getSheetAt(0);
//获得数据的总行数
int totalRowNum = sheet.getLastRowNum();
//获得总列数
int cellLength = sheet.getRow(0).getPhysicalNumberOfCells();
//获取表头
Row firstRow = sheet.getRow(0);
//获取反射类的所有字段
Field [] fields = clazz.getDeclaredFields();
//创建一个字段数组,用于和excel行顺序一致.
Field [] newFields = new Field[cellLength];
//获取静态方法.该方法是用于把excel的表头映射成实体字段名.
// convert方法是在反射的类中的静态方法.也防止Excel中的表头
//和反射的类的字段顺序不一致,导致数据读取在不正确的属性上
/**
public static String convert(String str){
switch (str){
case "身份证号码": return "idNumber";
case "名称": return "employeeName";
case "年龄": return "age";
case "出生日期": return "birthDate";
case "性别": return "gender";
default: return str;
}
}
*/
Method m = clazz.getDeclaredMethod("convert", String.class);
Object ob = clazz.newInstance();
for(int i=0;i<cellLength;i++) {
for (Field field:fields) {
Cell cell = firstRow.getCell(i);
//按照excel中的存储存放数组,以便后面遍历excel表格,数据一一对应.
if(m.invoke(ob,getXCellVal(cell)).equals(field.getName())){
newFields[i] = field;
continue;
}
}
}
//从第x行开始获取
for(int x = 1 ; x <= totalRowNum ; x++){
T object = clazz.newInstance();
//获得第i行对象
Row row = sheet.getRow(x);
//如果一行里的所有单元格都为空则不放进list里面
int a = 0;
for(int y=0;y<cellLength;y++){
if (!(row==null)) {
Cell cell = row.getCell(y);
if (cell == null) {
a++;
} else {
Field field = newFields[y];
String value = getXCellVal(cell);
if (value!=null && !value.equals("")){
//给字段设置值.
setValue(field,value,object);
}
}
}
}//for
if(a!=cellLength && row!=null){
resultList.add(object);
}
}
return resultList;
}catch (Exception e){
e.printStackTrace();
}
}
工具类中使用的方法如下:
/**
* 给字段赋值,判断值的类型,然后转化成实体需要的类型值.
* @param field 字段
* @param value 值
* @param object 对象
*/
private static void setValue(Field field, String value, Object object) {
try {
field.setAccessible(true);
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
if (field.getGenericType().toString().contains("Integer")){
field.set(object,Integer.valueOf(value));
}else if(field.getGenericType().toString().contains("String")){
field.set(object,value);
}else
if (field.getGenericType().toString().contains("Date")){
field.set(object,fmt.parse(value));
}
field.setAccessible(false);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* @param cell
* @return String
* 获取单元格中的值
*/
private static String getXCellVal(Cell cell) {
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
DecimalFormat df = new DecimalFormat("0.0000");
String val = "";
switch (cell.getCellType()) {
case XSSFCell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
val = fmt.format(cell.getDateCellValue()); //日期型
} else {
val = df.format(cell.getNumericCellValue()); //数字型
// 去掉多余的0,如最后一位是.则去掉
val = val.replaceAll("0+?$", "").replaceAll("[.]$","");
}
break;
case XSSFCell.CELL_TYPE_STRING: //文本类型
val = cell.getStringCellValue();
break;
case XSSFCell.CELL_TYPE_BOOLEAN: //布尔型
val = String.valueOf(cell.getBooleanCellValue());
break;
case XSSFCell.CELL_TYPE_BLANK: //空白
val = cell.getStringCellValue();
break;
case XSSFCell.CELL_TYPE_ERROR: //错误
val = "";
break;
case XSSFCell.CELL_TYPE_FORMULA: //公式
try {
val = String.valueOf(cell.getStringCellValue());
} catch (IllegalStateException e) {
val = String.valueOf(cell.getNumericCellValue());
}
break;
default:
val = cell.getRichStringCellValue() == null ? null : cell.getRichStringCellValue().toString();
}
return val;
}
3.以上是第一种导入的方法,第二种方法和第一种大致相同,只是返回的数据结构不同如下:
/** 导入excel表格,返回的List<Map<Integer, String>>数据结构.
* @param
* @return 返回list集合
* @throws Exception
*/
public static List<Map<Integer, String>> read(MultipartFile file) {
try{
//最终返回数据
List<Map<Integer, String>> list = new ArrayList<Map<Integer, String>>();
Workbook workbook = null;
InputStream is = file.getInputStream();
String name = file.getOriginalFilename().toLowerCase();
// 创建excel操作对象
if (name.contains(".xlsx") || name.contains(".xls")) {
workbook = WorkbookFactory.create(is);
}
//得到一个工作表
Sheet sheet = workbook.getSheetAt(0);
//获得数据的总行数
int totalRowNum = sheet.getLastRowNum();
//获得总列数
int cellLength = sheet.getRow(0).getPhysicalNumberOfCells();
Map<Integer, String> map = null;
//获得所有数据
//从第x行开始获取
for(int x = 0 ; x <= totalRowNum ; x++){
map = new HashMap<Integer, String>();
//获得第i行对象
Row row = sheet.getRow(x);
//如果一行里的所有单元格都为空则不放进list里面
int a = 0;
for(int y=0;y<cellLength;y++){
if (!(row==null)) {
Cell cell = row.getCell(y);
if (cell == null) {
map.put(y, "");
} else {
map.put(y, getXCellVal(cell));
}
if (map.get(y) == null || "".equals(map.get(y))) {
a++;
}
}
}//for
if(a!=cellLength && row!=null){
list.add(map);
}
}
return list;
}catch (Exception e){
e.printStackTrace();
}
}
4.测试用例直接上代码:
controller
@RequestMapping(value = "importExcel", method = RequestMethod.POST)
@ApiOperation(value = "员工导入", notes = "员工导入")
public Result employeeImport(@RequestParam("file") MultipartFile file) {
try {
employeeServer.employeeImport(file);
} catch (ServiceException e) {
e.printStackTrace();
return Results.error(e.getCode(), e.getMessage(), null);
}
return Results.success();
}
service
@Override
public void employeeImport(MultipartFile file) {
List<EmployeeDO> employeeDOList = ExcelUtils.readExcel(file, EmployeeDO.class);
employeeBO.batchExcel(employeeDOList);
}
5.返回的数据结构如上代码,第一种方法readExcel的的好处是比第二种read方法少了实体转化,不需要进行转化,返回的结构本身就是一个数组DTO实体,直接可以使用进行校验等,然后进行对象值拷贝,批量入库即可.但是如果excel中数据入库存在多个实体则不适用
6.read方法返回的则是一个数组Map结构,数据灵活,当excel表中导入入库多个实体,则可以转换这种结构,然后一一转化为对应入库的实体即可.
7.两个方法均经过测试,可用,但是没有经过性能测试.
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/5819.html