SpringBoot整合EasyPoi 实现Excel 导入与导出 导入数据校验

SpringBoot整合EasyPoi 实现Excel 导入与导出 导入数据校验

本文SpringBoot学习小营地是SpringBoot与EasyPoi的整合,通过此文章你可以做到基本数据的导入与导出以及导入数据校验

参考官网: easypoi

(1)EasyPoi整合前准备

1.项目依赖
         <!--easypoi与SpringBoot的整合包-->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-spring-boot-starter</artifactId>
            <version>4.1.2</version>
        </dependency>

也可以替换为:

        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>3.0.3</version>
        </dependency>
        excel操作
2.YML配置

由于我使用Google浏览器开始未配置时,导入文件报错:上传文件大小超出

所以本项目中目前定义了上传以及请求文件大小限制(具体可根据业务需求自行修改)

#HTML页面配置
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    #设置文件单次上传与请求无大小限制
  servlet:
    multipart:
      max-file-size: -1
      max-request-size: -1

本项目导入导出均可在PostMan软件中测试 当然 本文也编写了导入的HTML页面。
项目结构如下
在这里插入图片描述

(2)导入导出项目实体类

本文中做了深入导出(关联对象属性导出)具体实体类类注解用法请还是参考官网 easypoi

/**
 * @author : leilei
 * @date : 11:29 2020/2/22
 * @desc : name为在excel 名字 width:表格宽度  orderNum:处于excel第几行 默认为0   replace:替换 例如我的  true 替换为男  suffix:后缀  男生 女生
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    @Excel(name = "用户名", width = 15)
    private String username;

    @Excel(name = "密码", width = 15, orderNum = "4")
    private String password;

    @Excel(name = "性别", replace = {"男_true", "女_false"}, suffix = "生", width = 15, orderNum = "1")
    private Boolean sex;

    @Excel(name = "年龄")
    private Integer age;

    @Excel(name = "头像", width = 15, height = 15, type = 2, orderNum = "2")
    private String headimg;

    @Excel(name = "创建时间", exportFormat = "yyyy-MM-dd HH:mm:ss", importFormat ="yyyy-MM-dd HH:mm:ss",width = 25, orderNum = "3")
    private LocalDateTime createTime;
    /**连表查询时  需要深入导出关联对象属相*/
    @ExcelEntity
    private DepartMent departMent;
}

须深入导出 关联的对象

/**
 * @author : leilei
 * @date : 14:36 2020/2/22
 * @desc :
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DepartMent {
    @Excel(name = "部门")
    private String name;
}

(3)导入导出相关代码编写

(1)导出

由于本文并未连接数据库,所以 导入导出只能通过new 对象存集合的方式进行展示

创建查询用户列表接口
public interface IEasyPoiService {
    /**
     * 查询用户列表
     * @return
     */
    List<User> findUserList();
}
接口实现
public static String HeadImgUrl = "/static/head.jpg";

    @Override
    public List<User> findUserList() {
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 15; i++) {
            User user = new User();
            user.setUsername("leilei" + i);
            if (i % 2 == 0) {
                user.setSex(true);
                user.setDepartMent(new DepartMent("电商部门"));
            } else {
                user.setSex(false);
                user.setDepartMent(new DepartMent("传统部门"));

            }
            user.setPassword("leilei" + i);
            int age = new Random().nextInt(70 - 35) + 35;
            user.setAge(age);
            user.setHeadimg(HeadImgUrl);
            user.setCreateTime(LocalDateTime.now());
            users.add(user);
        }
        return users;
    }

因为本文是SpringBoot 项目,所有的静态资源都是放在src/main/java/resources/static下 直接导入时读取不到文件的(本文中,用户头像存放于本地static下),所以需要自定义一个文件读取类

自定义一个文件读取类
**
 * @author leilei
 * @date 2020/02/22
 * @desc 文件加载读取类, 根据路径加载指定文件   参照官网修改
 */
@Slf4j
public class FileLoaderImpl implements IFileLoader {


    @Override
    public byte[] getFile(String url) {
        InputStream fileis = null;
        ByteArrayOutputStream baos = null;
        try {

            //判断是否是网络地址
            if (url.startsWith("http")) {
                URL urlObj = new URL(url);
                URLConnection urlConnection = urlObj.openConnection();
                urlConnection.setConnectTimeout(30);
                urlConnection.setReadTimeout(60);
                urlConnection.setDoInput(true);
                fileis = urlConnection.getInputStream();
            } else {
                //先用绝对路径查询,再查询相对路径
                try {
                    fileis = new FileInputStream(url);
                } catch (FileNotFoundException e) {
                    //获取项目文件
                    fileis = FileLoaderImpl.class.getClassLoader().getResourceAsStream(url);
                    if (fileis == null) {
                        fileis = FileLoaderImpl.class.getResourceAsStream(url);
                    }
                }
            }
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fileis.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
            return baos.toByteArray();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(fileis);
            IOUtils.closeQuietly(baos);
        }
        log.error(fileis + "这个路径文件没有找到,请查询");
        return null;
    }
}
文件监听读取类
/**
 * @author leilei
 * @date 2020/02/22
 * @desc  使自定义的文件加载类生效  (本地图片)
 */
@Component
public class ExcelListener implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        POICacheManager.setFileLoader(new FileLoaderImpl());
    }
}
编写导出控制层 导出用户列表到Excel中
 @Autowired
    private IEasyPoiService easyPoiService;
 
  /**
     * 导出1 使用本身代码  导出须校验的数据集
     */
    @RequestMapping("/outexcel")
    public String download(ModelMap map) {
        List<UserVo> list = easyPoiService.findUserVoList();

        //导出基本信息的配置
        ExportParams params = new ExportParams("用户列表", "用户", ExcelType.XSSF);
//        params.setFreezeCol(2);//冻结的列
        /**excel文件要导出的数据*/
        map.put(NormalExcelConstants.DATA_LIST, list);
        /**excel文件导出的实体*/
        map.put(NormalExcelConstants.CLASS, UserVo.class);
        /**excel文件参数*/
        map.put(NormalExcelConstants.PARAMS, params);
        /**excel文件名称*/
        map.put(NormalExcelConstants.FILE_NAME, "用户列表");
        return NormalExcelConstants.EASYPOI_EXCEL_VIEW;

    }

 /**
     * 导出2  使用网上excel导出导入工具类  无需校验的导入导出
     *
     * @param response
     */
    @RequestMapping(value = "/export")
    public void exportExcel(HttpServletResponse response) throws IOException {

        List<User> users = easyPoiService.findUserList();
        ExcelUtils.exportExcel(users, "用户信息表", "用户信息", User.class, "用户信息", response);
    }

在这里插入图片描述

贴出导出导入工具类代码
package com.leilei.util;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.UUID;

/**
 * excel 工具类
 *
 * @author easypoi
 * @date 2020/02/22
 */
public class ExcelUtils {
    /**
     * 导出excel到
     *
     * @param list           数据
     * @param title          表头
     * @param sheetName      sheetName
     * @param pojoClass      解析的对象类型
     * @param fileName       文件名称
     * @param isCreateHeader 是否创建表头
     * @return 文件路径
     */
    public static String exportExcelToFile(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader) {
        OutputStream out = null;
        Workbook workbook = null;
        try {
            ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
            exportParams.setCreateHeadRows(isCreateHeader);
            fileName = encodingFilename(fileName);

            out = new FileOutputStream(getAbsoluteFile(fileName));

            workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
            workbook.write(out);
            return fileName;
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * excel 导出到文件
     *
     * @param list      数据
     * @param title     表头
     * @param sheetName sheet名称
     * @param pojoClass pojo类型
     * @param fileName  文件名
     * @return 文件路径
     */
    public static String exportExcelToFile(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName) {
        return exportExcelToFile(list, title, sheetName, pojoClass, fileName, true);
    }

    /**
     * excel 导出到文件
     *
     * @param list      数据
     * @param title     表头
     * @param sheetName sheet名称
     * @param pojoClass pojo类型
     * @return 文件路径
     */
    public static String exportExcelToFile(List<?> list, String title, String sheetName, Class<?> pojoClass) {
        return exportExcelToFile(list, title, sheetName, pojoClass, title, true);
    }

    /**
     * excel 导出到文件
     *
     * @param list      数据
     * @param fileName  文件名
     * @param pojoClass pojo类型
     * @return 文件路径
     */
    public static String exportExcelToFile(List<?> list, String fileName, Class<?> pojoClass) {
        return exportExcelToFile(list, fileName, fileName, pojoClass, fileName, true);
    }

    /**
     * excel 导出
     *
     * @param list           数据
     * @param title          标题
     * @param sheetName      sheet名称
     * @param pojoClass      pojo类型
     * @param fileName       文件名称
     * @param isCreateHeader 是否创建表头
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) throws IOException {
        ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
        exportParams.setCreateHeadRows(isCreateHeader);
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * excel 导出
     *
     * @param list      数据
     * @param title     标题
     * @param sheetName sheet名称
     * @param pojoClass pojo类型
     * @param fileName  文件名称
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));
    }

    /**
     * excel 导出
     *
     * @param list         数据
     * @param pojoClass    pojo类型
     * @param fileName     文件名称
     * @param response
     * @param exportParams 导出参数
     */
    public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, ExportParams exportParams, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * excel 导出
     *
     * @param list     数据
     * @param fileName 文件名称
     * @param response
     */
    public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, fileName, response);
    }

    /**
     * 默认的 excel 导出
     *
     * @param list         数据
     * @param pojoClass    pojo类型
     * @param fileName     文件名称
     * @param response
     * @param exportParams 导出参数
     */
    private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * 默认的 excel 导出
     *
     * @param list     数据
     * @param fileName 文件名称
     * @param response
     */
    private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * 下载
     *
     * @param fileName 文件名称
     * @param response
     * @param workbook excel数据
     */
    private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + "." + ExcelTypeEnum.XLSX.getValue(), "UTF-8"));
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 导入
     *
     * @param filePath   excel文件路径
     * @param titleRows  标题行
     * @param headerRows 表头行
     * @param pojoClass  pojo类型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (StringUtils.isBlank(filePath)) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setNeedSave(true);
        params.setSaveUrl("/excel/");
        try {
            return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("模板不能为空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 导入
     *
     * @param file      excel文件
     * @param pojoClass pojo类型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) throws IOException {
        return importExcel(file, 1, 1, pojoClass);
    }

    /**
     * excel 导入
     *
     * @param file       excel文件
     * @param titleRows  标题行
     * @param headerRows 表头行
     * @param pojoClass  pojo类型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        return importExcel(file, titleRows, headerRows, false, pojoClass);
    }

    /**
     * excel 导入
     *
     * @param file       上传的文件
     * @param titleRows  标题行
     * @param headerRows 表头行
     * @param needVerify 是否检验excel内容
     * @param pojoClass  pojo类型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, boolean needVerify, Class<T> pojoClass) throws IOException {
        if (file == null) {
            return null;
        }
        try {
            return importExcel(file.getInputStream(), titleRows, headerRows, needVerify, pojoClass);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 导入
     *
     * @param inputStream 文件输入流
     * @param titleRows   标题行
     * @param headerRows  表头行
     * @param needVerify  是否检验excel内容
     * @param pojoClass   pojo类型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, boolean needVerify, Class<T> pojoClass) throws IOException {
        if (inputStream == null) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setSaveUrl("/excel/");
        params.setNeedSave(true);
        params.setNeedVerify(needVerify);
        try {
            return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("excel文件不能为空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }


    /**
     * 获取下载路径
     *
     * @param downloadPath 文件名称
     */
    private static String getAbsoluteFile(String downloadPath) {
        downloadPath = "/excel/" + downloadPath;
        File desc = new File(downloadPath);
        if (!desc.getParentFile().exists()) {
            desc.getParentFile().mkdirs();
        }
        return downloadPath;
    }

    /**
     * 编码文件名
     */
    private static String encodingFilename(String filename) {
        filename = UUID.randomUUID().toString() + "_" + filename + "." + ExcelTypeEnum.XLSX.getValue();
        return filename;
    }

    /**
     * Excel 类型枚举
     */
    enum ExcelTypeEnum {
        XLS("xls"), XLSX("xlsx");
        private String value;

        ExcelTypeEnum(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }
}

在浏览器中访问localhost:8080/export 或者 localhost:8080/outexcel 即可将自定义的十多条用户数据导出到Excel表格中 打开即可查看

(2)导入
导入HTML页面简单编写
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>poi导入测试页面</title>
</head>
<body>
<h2><p> poi导入测试页面</p></h2>
<div>
    <form method="POST" enctype="multipart/form-data" action="/import/import1">
        <p>
            文件:<input type="file" name="file"/>
            <input type="submit" value="导入1 普通导入"/>
        </p>
    </form>
</div>

<div>
    <form method="POST" enctype="multipart/form-data" action="/import/import2">
        <p>
            文件:<input type="file" name="file"/>
            <input type="submit" value="导入2 文件内容校验"/>
        </p>
    </form>
</div>

</body>
</html>
普通导入

使用本文提供的Excel工具类进行导入

    /**
     * 普通导入
     *
     * @param file
     */
    @PostMapping("/import1")
    public Object importExcel(MultipartFile file) throws IOException {
        List<User> users = ExcelUtils.importExcel(file, User.class);
        users.forEach(e -> System.out.println(e));
        return users;
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-frRC5yYj-1582366579782)(C:\Users\leilei\AppData\Roaming\Typora\typora-user-images\1582364432867.png)]

校验导入

新建导入导出实体类

Easypoi 中的一些注解 校验

@NotNull 表示导入的此字段值不能为空

@Max 表示此字段值 最大值

@Min 表示此字段值 最小值

还可以正则校验等

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVo {
    @Excel(name = "用户名", width = 15)
    @NotNull(message = "用户名不能为空哦!!")
    private String username;

    @Excel(name = "密码", width = 15, orderNum = "4")
    @NotNull(message = "密码不能为空!!")
    private String password;

    @Excel(name = "性别", replace = {"男_true", "女_false"}, suffix = "生", width = 15, orderNum = "1")
    private Boolean sex;

    @Excel(name = "年龄")
    @Max(value = 65,message = "年龄太大了,不适合工作哦")
    private Integer age;

    @Excel(name = "头像", width = 15, height = 15, type = 2, orderNum = "2")
    private String headimg;

    @Excel(name = "创建时间", exportFormat = "yyyy-MM-dd HH:mm:ss", importFormat ="yyyy-MM-dd HH:mm:ss",width = 25, orderNum = "3")
    private LocalDateTime createTime;
    /**连表查询时  需要深入导出关联对象属相*/
    @ExcelEntity
    private DepartMent departMent;

自定义校验规则

本文自定义的校验规则 是校验用户名不可重复(其他校验规则可根据自己的业务场景来定) 须编写一个类 实现IExcelVerifyHandler 并覆写其中的方法

/**
 * @author : leilei
 * @date : 15:36 2020/2/22
 * @desc :自定义excel校验名字是否重复  实现IExcelVerifyHandler接口
 */
@Component
public class MyExcelverifiyName implements IExcelVerifyHandler<UserVo> {
    @Autowired
    private ICheckUserVoService checkUserVoService;

    @Override
    public ExcelVerifyHandlerResult verifyHandler(UserVo userVo) {
        //设置默认验证为true
        ExcelVerifyHandlerResult excelVerifyHandlerResult = new ExcelVerifyHandlerResult(true);
        if (StringUtils.isNotBlank(userVo.getUsername())) {
            UserVo one = checkUserVoService.findOneByname(userVo.getUsername());
            //查询不为空则说明数据库中用户已存在,此次录入为重复录入
            if (one != null) {
                excelVerifyHandlerResult.setSuccess(false);
                excelVerifyHandlerResult.setMsg("对不起,此用户已存在,请不要重复提交");
            }
        }
        return excelVerifyHandlerResult;
    }
}

校验规则实现类与接口编写

/**
 * @author : leilei
 * @date : 15:51 2020/2/22
 * @desc : 自定义校验规则接口
 */
public interface ICheckUserVoService {
    UserVo findOneByname(String username);
}
/**
 * @author : leilei
 * @date : 15:51 2020/2/22
 * @desc : 校验用户是否重复
 */
@Service
public class CheckUserVoServiceImpl implements ICheckUserVoService {
    @Override
    public UserVo findOneByname(String username) {
        /**
         * 虚假数据  由于我这里是根据名字查  那么我模拟一个对象返回即可 (实际开发可根据身份证去数据库查询)
         */
        if (username .equals("leilei0") ) {
            return new UserVo("leilei0", "1", true, 22, "aaa", LocalDateTime.now(), new DepartMent("dianshan"));
        }
        return null;
    }
}

控制层

   /**
     * 文件校验导入
     *
     * @return
     */
    @RequestMapping("/import2")
    public Object upload(MultipartFile file,HttpServletResponse resp) throws Exception {
        //导入的基本配置
        ImportParams params = new ImportParams();
        //表头一行
        params.setHeadRows(1);
        //标题一行
        params.setTitleRows(1);
        //代表导入这里是需要验证的(根据字段上的注解校验)
        params.setNeedVerify(true);
        //设及一个自定义校验 (自定义校验名字不可重复)
        params.setVerifyHandler(excelVerifyHandler);
        //使用框架自身导入工具
        ExcelImportResult<UserVo> result = ExcelImportUtil.importExcelMore(file.getInputStream(), UserVo.class, params);
        //导入成功的数据
        List<UserVo> list = result.getList();
        //失败结果集
        List<UserVo> failList = result.getFailList();
        //拿到导出失败的工作簿
        Workbook failWorkbook = result.getFailWorkbook();
        //验证是否有失败的数据
        if (result.isVerifyFail()) { 
            ServletOutputStream fos = resp.getOutputStream();
            //mime类型
            resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            resp.setHeader("Content-disposition", "attachment;filename=error.xlsx");
            result.getFailWorkbook().write(fos);
            fos.close();
        }
        return failList;
    }

导入后 会下载一个Excel 文件 文件中会包含导入失败的数据以及失败原由
在这里插入图片描述

结语

SpringBoot 与EasyPoi 导入导出Excel 就到这里了 附上我的源码地址: springboot-easypoi

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章