• Java进行excel的导入导出操作


    excel表格的导出导入在业务中经常会遇到,下面介绍hutool和easyExcel两种操作excel的工具

    测试的实体类

    通过mybatis-plus生成的,用于导出数据的实体类

    
    @Getter
    @Setter
    @TableName("device_info")
    @ApiModel(value = "DeviceInfo对象", description = "")
    public class DeviceInfo implements Serializable {
    
        @ApiModelProperty("设备ID")
        @TableId(value = "device_id",type = IdType.ASSIGN_UUID)
        private String deviceId;
    
        @ApiModelProperty("设备名称")
        @TableField("device_name")
        private String deviceName;
    
        @ApiModelProperty("设备编号")
        @TableField("device_no")
        private String deviceNo;
    
        @ApiModelProperty("设备型号")
        @TableField("device_model")
        private String deviceModel;
    
        @ApiModelProperty("新建时间")
        @TableField("create_time")
        private LocalDateTime createTime;
    
        @ApiModelProperty("更新时间")
        @TableField("update_time")
        private LocalDateTime updateTime;
    
        @ApiModelProperty("创建人")
        @TableField("created_by")
        private String createdBy;
    
        @ApiModelProperty("更新人")
        @TableField("updated_by")
        private String updatedBy;
    
        @ApiModelProperty("版本")
        @TableField("version")
        private Long version;
    
    }
    

    Mapper文件

    package com.example.demo.mapper;
    
    import com.example.demo.entity.DeviceInfo;
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import org.apache.ibatis.annotations.Mapper;
    
    
    @Mapper
    public interface DeviceInfoMapper extends BaseMapper {
    
    }
    

    Mapper的xml文件

    
    mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.demo.mapper.DeviceInfoMapper">
    
        
        <resultMap id="BaseResultMap" type="com.example.demo.entity.DeviceInfo">
            <id column="device_id" property="deviceId" />
            <result column="device_name" property="deviceName" />
            <result column="device_no" property="deviceNo" />
            <result column="device_model" property="deviceModel" />
            <result column="create_time" property="createTime" />
            <result column="update_time" property="updateTime" />
            <result column="created_by" property="createdBy" />
            <result column="updated_by" property="updatedBy" />
            <result column="version" property="version" />
        resultMap>
    
        
        <sql id="Base_Column_List">
            device_id, device_name, device_no, device_model, create_time, update_time, created_by, updated_by, version
        sql>
    
    mapper>
    

    IService接口

    package com.example.demo.service;
    
    import com.example.demo.entity.DeviceInfo;
    import com.baomidou.mybatisplus.extension.service.IService;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    
    public interface IDeviceInfoService extends IService {
    
        void exportExcel(HttpServletResponse response) throws IOException;
    
        boolean importExcel(InputStream inputStream);
    }
    

    1、使用hutool导出

    优点:hutool有很多工具类,包括ExcelWrite和ExcelReader工具类,写代码也很简洁,我觉得简单的导入导出优先可以使用
    缺点:导出不支持excel模板,很难将一些特定数据放在特定的单元格中,导入不好读表格的特殊信息,如批注信息等

    增加依赖

    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
        <version>5.7.22version>
    dependency>
    

    导入的表格

    导出的表格

    如果要好看的格式、列宽、行距,可以通过以下代码设置单元格的格式,

    StyleSet style = writer.getStyleSet();
    style.getHeadCellStyle().setFillBackgroundColor(IndexedColors.WHITE.getIndex());
    writer.getCell(0,0).getCellStyle().setFillForegroundColor(IndexedColors.WHITE.getIndex());
    

    代码实现

    以下是controller类

    package com.example.demo.controller;
    
    
    import com.example.demo.service.IDeviceInfoService;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    
    @RestController
    @RequestMapping("/device-info")
    public class DeviceInfoController {
    
        @Resource
        private IDeviceInfoService deviceInfoService;
    
        @GetMapping("/exportExcel")
        @ApiOperation(value = "导出模板表格测试")
        public void exportExcel(HttpServletResponse response) throws IOException {
            deviceInfoService.exportExcel(response);
        }
    
        @PostMapping("importExcel")
        @ApiOperation(value = "导入表格测试")
        public boolean importExcel(MultipartFile file) throws IOException {
            return deviceInfoService.importExcel(file.getInputStream());
            }
    }
    

    以下是进行简单导入导出操作的Service

    package com.example.demo.service.impl;
    
    import cn.hutool.poi.excel.ExcelReader;
    import cn.hutool.poi.excel.ExcelUtil;
    import cn.hutool.poi.excel.ExcelWriter;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.example.demo.entity.DeviceInfo;
    import com.example.demo.mapper.DeviceInfoMapper;
    import com.example.demo.service.IDeviceInfoService;
    import io.swagger.annotations.ApiModelProperty;
    import org.springframework.stereotype.Service;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Field;
    import java.net.URLEncoder;
    import java.time.LocalDate;
    import java.util.List;
    
    
    @Service
    public class DeviceInfoServiceImpl extends ServiceImpl implements IDeviceInfoService {
    
    
        @Override
        public void exportExcel(HttpServletResponse response) throws IOException {
    
            //导出的表格名称
            String targetFileName = "测试导出表格" + LocalDate.now();
            String suffix = ".xlsx";
    
            //设置接口响应结果格式
            response.reset();
            //response为HttpServletResponse对象
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(targetFileName + suffix, "UTF-8"));
    
            List deviceInfoList = this.list();
    
            // 通过工具类创建writer,默认创建xls格式
            ExcelWriter writer = ExcelUtil.getWriter();
    
            //获取注解的值,跟excel表的表头对应
            Field[] fields = DeviceInfo.class.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                String header = fields[i].getDeclaredAnnotation(ApiModelProperty.class).value();
                writer.addHeaderAlias( fields[i].getName(),header);
            }
            //可以新建sheet,将数据导出成多个sheet
            writer.setSheet("设备信息");
    // 一次性写出内容,使用默认样式
            writer.write(deviceInfoList);
    
            //根据单元格内容自动调整列宽
            writer.autoSizeColumnAll();
    //out为OutputStream,需要写出到的目标流
            writer.flush(response.getOutputStream());
    // 关闭writer,释放内存
            writer.close();
    
        }
    
        @Override
        public boolean importExcel(InputStream inputStream) {
            ExcelReader reader = ExcelUtil.getReader(inputStream, 0);
            //获取注解的值,跟excel表的表头对应
            Field[] fields = DeviceInfo.class.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                String header = fields[i].getDeclaredAnnotation(ApiModelProperty.class).value();
                reader.addHeaderAlias(header, fields[i].getName());
            }
    
            List deviceInfoList = reader.read(0, 1, DeviceInfo.class);
    
            return this.saveOrUpdateBatch(deviceInfoList);
        }
    }
    

    2、使用easyExcel导出

    增加依赖

    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>easyexcelartifactId>
        <version>3.2.1version>
    dependency>
    

    导入的表格

    导出的表格

    预先做好的导出模板


    这是进行导出的模板,需要在每一个单元格写上实体的字段名称,程序才能精确将数据填到相应的位置
    如{.deviceId}在字段名称前有个 . 表示数据是一个List,如{listSize}表示数据只是一个单元格的值

    导出的模板文件放在资源下面

    导出的表格结果


    代码实现

    Controller类

    package com.example.demo.controller;
    
    
    import com.alibaba.excel.EasyExcel;
    import com.example.demo.entity.DeviceInfo;
    import com.example.demo.listener.DeviceInfoListener;
    import com.example.demo.service.IDeviceInfoService;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    
    @RestController
    @RequestMapping("/device-info")
    public class DeviceInfoController {
    
        @Resource
        private IDeviceInfoService deviceInfoService;
    
        @GetMapping("/exportExcel")
        @ApiOperation(value = "导出模板表格测试")
        public void exportExcel(HttpServletResponse response) throws IOException {
            deviceInfoService.exportExcel(response);
        }
    
        @PostMapping("importExcel")
        @ApiOperation(value = "导入表格测试")
        public boolean importExcel(MultipartFile file) throws IOException {
            EasyExcel.read(file.getInputStream(),DeviceInfo.class,new DeviceInfoListener(deviceInfoService)).sheet().doRead();
            return true;
        }
    }
    

    实现导出逻辑的Service类

    package com.example.demo.service.impl;
    
    import cn.hutool.poi.excel.ExcelReader;
    import cn.hutool.poi.excel.ExcelUtil;
    import com.alibaba.excel.EasyExcelFactory;
    import com.alibaba.excel.ExcelWriter;
    import com.alibaba.excel.write.metadata.WriteSheet;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.example.demo.entity.DeviceInfo;
    import com.example.demo.mapper.DeviceInfoMapper;
    import com.example.demo.service.IDeviceInfoService;
    import io.swagger.annotations.ApiModelProperty;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.stereotype.Service;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Field;
    import java.net.URLEncoder;
    import java.time.LocalDate;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Service
    public class DeviceInfoServiceImpl extends ServiceImpl implements IDeviceInfoService {
    
    
        @Override
        public void exportExcel(HttpServletResponse response) throws IOException {
            //导出的表格名称
            String targetFileName = "测试导出表格" + LocalDate.now();
            String suffix = ".xlsx";
            //根据相对路径获取模板表格的内容
            ClassPathResource resource = new ClassPathResource("template/测试导出模板表格.xlsx");
            File bathFile = resource.getFile();
            //设置接口响应结果格式
            response.reset();
            //response为HttpServletResponse对象
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(targetFileName + suffix, "UTF-8"));
    
            List deviceInfoList = this.list();
    
            Map map = new HashMap<>();
            map.put("listSize",deviceInfoList.size() );
            map.put("nowDate",LocalDate.now() );
    
            //将模板复制到目标表格中
            ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).withTemplate(bathFile).build();
            //将列表的值写入表格中,要求实体类的字段名称与模板中的名称一致
            WriteSheet sheet = EasyExcelFactory.writerSheet().build();
            excelWriter.fill(map,sheet);
            excelWriter.fill(deviceInfoList, sheet);
            excelWriter.finish();
        }
        
    }
    

    导入需要写一个数据监听类,实现readListener接口

    package com.example.demo.listener;
    
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.read.listener.ReadListener;
    import com.alibaba.excel.util.ListUtils;
    import com.alibaba.fastjson.JSON;
    import com.example.demo.entity.DeviceInfo;
    import com.example.demo.service.IDeviceInfoService;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.List;
    
    @Slf4j
    public class DeviceInfoListener implements ReadListener {
    
        /**
         * 每隔100条存储数据库,然后清理list ,方便内存回收
         */
        private static final int BATCH_COUNT = 100;
        /**
         * 缓存的数据
         */
        private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    
        private IDeviceInfoService deviceInfoService;
    
        public DeviceInfoListener( ) {
        }
    
        public DeviceInfoListener(IDeviceInfoService deviceInfoService) {
            this.deviceInfoService = deviceInfoService;
        }
        @Override
        public void invoke(DeviceInfo data, AnalysisContext context) {
            log.info("解析到一条数据:{}", JSON.toJSONString(data));
            cachedDataList.add(data);
            // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
            if (cachedDataList.size() >= BATCH_COUNT) {
                saveData();
                // 存储完成清理 list
                cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
            }
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
    // 这里也要保存数据,确保最后遗留的数据也存储到数据库
            saveData();
            log.info("所有数据解析完成!");
        }
    
        /**
         * 加上存储数据库
         */
        private void saveData() {
            log.info("{}条数据,开始存储数据库!", cachedDataList.size());
            deviceInfoService.saveOrUpdateBatch(cachedDataList);
            log.info("存储数据库成功!");
        }
    }
    
  • 相关阅读:
    Java文件操作和IO
    WPF 如何让xmal的属性换行显示 格式化
    【JAVA】读取classpath下的文件
    R语言中如何查看已安装的R包
    Kubeadm搭建kubernetes集群
    【算法】spfa算法(spfa判断负环)
    实习报告1——人脸三维重建方法
    技术岗/算法岗面试如何准备?5000字长文、6个角度以2023秋招经历分享面试经验
    【手把手】光说不练假把式,这篇全链路压测实践探索
    浅谈免杀下的持久化
  • 原文地址:https://www.cnblogs.com/davidFB/p/18136806