Java 上传Excel 文档解析成对象

日常开发过程中经常有需要上传Excel文档的需求,今天就给大家总结一篇比较好用的API接口。

1、首先引入jar

       <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.14</version>
        </dependency>

2、工具类伪代码:

import com.google.common.collect.Maps;
import com.nandao.demo.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;

/**
 * @author wanghuainan
 * @date 2017/9/6 16:05
 */
@Slf4j
public class PoiUtil<T> {
    Class<T> clazz;

    /**
     * 有参构造函数
     * @param clazz
     */
    public PoiUtil(Class<T> clazz) {
        this.clazz = clazz;
    }

    public List<T> importExcel(Integer sheetIndex, MultipartFile file) throws Exception {
        int maxCol = 0;
        List<T> list = new ArrayList<T>();//定义范型的集合
        Workbook workbook = null;
        if (file.getOriginalFilename().endsWith("xls")) {
            workbook = new HSSFWorkbook(file.getInputStream());
        } else {
            workbook = new XSSFWorkbook(file.getInputStream());
        }
        if (sheetIndex == null) {
            sheetIndex = 0;
        }
        Sheet sheet = workbook.getSheetAt(sheetIndex);
        int rows = sheet.getPhysicalNumberOfRows();
        // 有数据时才处理
        if (rows > 0) {
            /**
             * 通过反射取出实体的每个参数
             */
            List<Field> allFields = getMappedFiled(clazz, null);

            // 定义一个map用于存放列的序号和field.
            Map<Integer, Field> fieldsMap = Maps.newHashMap();
            Row titleRow = sheet.getRow(0);
            for (Field field : allFields) {//遍历每个参数
                // 将有注解的field存放到map中.
                if (field.isAnnotationPresent(ExcelVOAttribute.class)) {//参数上是否有注解
                    /**
                     * 取出注解,并取出注解里面的参数,逐个匹配
                     */
                    ExcelVOAttribute attr = field.getAnnotation(ExcelVOAttribute.class);
                    // 获得列号
                    int col = getExcelCol(attr.column());
                    maxCol = Math.max(col, maxCol);
                    // 设置类的私有字段属性可访问.
                    field.setAccessible(true);
                    fieldsMap.put(col, field);
                    //校验标题必填列是否与模板一致
                    if (attr.required()) {
                        Cell cell = titleRow.getCell(col);
                        String title = null;
                        if (cell != null) {
                            title = cell.getStringCellValue();
                        }//校验标题和模版中是否一致
                        if (title == null || !title.trim().equals(attr.templateName())) {
                            throw new BizException(ResultCode.PARAM_ERROR.getCode(), "请检查导入模板,第" + (col + 1) + "列标题与模板不一致!");
                        }
                    }
                }
            }
            // 从第2行开始取数据,默认第一行是表头.
            for (int i = 1; i < rows; i++) {
                Row row = sheet.getRow(i);

                //判断空行
                if (isEmptyRow(row)) {
                    break;
                }

                int cellNum = maxCol;
                T entity = null;
                for (int j = 0; j <= cellNum; j++) {
                    Cell cell = row.getCell(j);
                    Field field = fieldsMap.get(j);
                    ExcelVOAttribute attr = field.getAnnotation(ExcelVOAttribute.class);
                    String filedName = attr.templateName();
                    Class<?> fieldType = field.getType();
                    if (cell == null) {
                        if (attr.required()) {
                            throw new BizException(ResultCode.PARAM_ERROR.getCode(), filedName + "不能为空,第" + (i + 1) + "行数据" + filedName + "为空!");
                        } else {
                            continue;
                        }
                    }
                    int cellType = cell.getCellType();
                    String c = "";
                    if (cellType == Cell.CELL_TYPE_NUMERIC) {
                        NumberFormat numberFormat = NumberFormat.getInstance();//防止数值转换成Doule小数点类型
                        // 不显示千位分割符,否则显示结果会变成类似1,234,567,890
                        numberFormat.setGroupingUsed(false);
                        if (String.valueOf(cell.getNumericCellValue()).indexOf("E") == -1) {
                            c = String.valueOf(numberFormat.format(cell.getNumericCellValue()));
                        } else {
                            if (String.valueOf(cell.getNumericCellValue()).indexOf("-") == -1) {
                                BigDecimal bd = new BigDecimal(String.valueOf(cell.getNumericCellValue()));
                                c = String.valueOf(bd.longValueExact());
                            } else {
                                BigDecimal bd = new BigDecimal(String.valueOf(cell.getNumericCellValue()));
                                c = bd.toString();
                            }
//                            c = String.valueOf(new DecimalFormat("#").format(cell.getNumericCellValue()));
                        }
                    } else if (cellType == Cell.CELL_TYPE_BOOLEAN) {
                        c = String.valueOf(cell.getBooleanCellValue()).trim();
                    } else {
                        c = cell.getStringCellValue().trim();
                    }

                    if (c == null || StringUtils.isEmpty(c)) {
                        if (attr.required()) {
                            throw new BizException(ResultCode.PARAM_ERROR.getCode(), filedName + "不能为空,第" + (i + 1) + "行数据" + filedName + "为空!");
                        } else {
                            continue;
                        }
                    }
                    try {
                        entity = (entity == null ? clazz.newInstance() : entity);
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    if (field == null) {
                        continue;
                    }
                    try {
                        // 取得类型,并根据对象类型设置值.
                        if (String.class == fieldType) {
                            field.set(entity, c);
                        } else if ((Integer.TYPE == fieldType)
                                || (Integer.class == fieldType)) {
                            field.set(entity, Integer.valueOf(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)));
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new BizException(ResultCode.PARAM_ERROR.getCode(), "第" + (i + 1) + "行数据" + filedName + "格式非法,请检查EXCEL");
                    }
                }
                if (entity != null) {
                    list.add(entity);
                }
            }
        }
        return list;
    }
}

通过反射取参数的代码:

 
    private List<Field> getMappedFiled(Class clazz, List<Field> fields) {
        if (fields == null) {
            fields = new ArrayList<Field>();
        }

        Field[] allFields = clazz.getDeclaredFields();// 得到所有定义字段
        // 得到所有field并存放到一个list中.
        for (Field field : allFields) {
            if (field.isAnnotationPresent(ExcelVOAttribute.class)) {
                fields.add(field);
            }
        }
        if (clazz.getSuperclass() != null
                && !clazz.getSuperclass().equals(Object.class)) {
            getMappedFiled(clazz.getSuperclass(), fields);
        }

        return fields;
    }

 自定义注解:

/**
 * @author wanghuainan
 * @date 2017/9/6 16:10
 */
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target( { java.lang.annotation.ElementType.FIELD })
public @interface ExcelVOAttribute {

    /**
     * 导出到Excel中的名字.
     */
    String name() default "";

    /**
     * 下载模板中的名字.
     */
    String templateName() default "";

    /**
     * 配置列的名称,对应A,B,C,D....
     */
    String column();

    boolean required() default false;
    /**
     * 导出到Excel中的宽度.
     */
    String width() default "";

    /**
     * 提示信息
     */
    String prompt() default "";

    /**
     * 设置只能选择不能输入的列内容.
     */
    String[] combo() default {};

    /**
     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
     */
    boolean isExport() default true;
}

3、控制层的代码:

/**
     * 上传excel表格
     * @param file
     * @return
     */
    @RequestMapping("uploadExcel")
    public R uploadExcel(@RequestParam("file") MultipartFile file){
        /**
         * 通过有参构造函数创建工具类
         */
        PoiUtil<UserInfoVoTemplate> userVoTemplatePoi = new PoiUtil(UserInfoVoTemplate.class);
        List<UserInfoVoTemplate> userVoTemplateExcels = null;
        List<Long> userIdList = new ArrayList<>();
        try {
            /**
             * 正式取出excel中的内容生成list集合
             */
            userVoTemplateExcels = userVoTemplatePoi.importExcel(0, file);
            /**
             * 通过流式变成取某个参数和成集合
             */
            userIdList = userVoTemplateExcels.stream().map(u ->u.getUserId()).collect(Collectors.toList());
            log.info("上传的用户id:[{}]",userIdList.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return R.ok();
    }

定义的对象:

package com.nandao.demo.entity;

import com.nandao.demo.common.ExcelVOAttribute;
import lombok.Data;

import java.io.Serializable;

/**
 * @author wanghuainan
 * @date 2017/9/06
 */
@Data
public class UserInfoVoTemplate implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 用户id
     */
    @ExcelVOAttribute(templateName = "用户主键", name = "用户主键", column = "A", width = "4000", required = true)
    private Long userId;


}

集成结束,然后通过postman,就可以测试了,后期我们还会分享一篇关于生成Excel文档的文章,敬请期待!

上一篇:haoop 断电后导致block文件损坏


下一篇:[极客大挑战 2019]Upload