关于springboot+vue的excel导入导出

news2024/11/28 23:42:20

       在做项目的过程中,外面经常会遇到一个问题,怎么才能把excel表中的数据实现批量的导入导出,使用的是EasyExcel进行操作。

一、项目前准备

1、依赖导入

在pom文件中添加对应的依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.3</version>
</dependency>

2、实体类准备

原数据库字段过多,这边就截取一部分进行展示

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("emp_user")
public class EmpUserEntity implements Serializable {
	private static final long serialVersionUID = 1L;
	/**
	 * 人员id
	 */
	@ExcelIgnore
	private Long id;
	/**
	 * 用户ID
	 */
	@ExcelIgnore
	private Long userId;
	/**
	 * 密码
	 */
	@TableField(exist = false)
	@ExcelIgnore
	private String password;
	/**
	 * 姓名
	 */
	@ExcelProperty(value = {"姓名"}, index = 0)
	private String name;
	/**
	 * 性别:1:男  0:女
	 */
	@ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class)
	//静态下拉框
	@ExcelSelected(source = {"男","女"})
	private Integer gender;
	/**
	 * 年龄(计算退休时间)
	 */
	@ExcelProperty(value = {"年龄"}, index = 2)
	private Integer age;
	/**
	 * 电话
	 */
	@ExcelProperty(value = {"电话"}, index = 3)
	@ColumnWidth(20)
	private String tel;
	/**
	 * 邮箱
	 */
	@ExcelProperty(value = {"邮箱"}, index = 4)
	@ColumnWidth(25)
	private String email;
}

注释解释:

@ExcelIgnore:EasyExcel框架中的注解,用于标识实体类中不需要导出或导入的属性。

@ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class):指定实体类与表格之间的映射关系

       value表示excel表格的标题列。

       index表示excel表格中的列索引。

       converter = SexConvert.class:指定类型转换器,将Excel表格中的数据转换成Java对象。
@ExcelSelected(source = {"男","女"}):导出下拉框自定义注解

3、类型转换器

       有些数据外面存入数据库的是数字,例如一般男生我们选用1,女生选用0,当时当我们进行表的输出和输入时,excel表中用的是男、女,我们采用类型转换器进行数据的转化。

(1)实体类中添加注释
@ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class)
//SexConvert.class定义类转化器的类
(2)转换类的编写
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

public class SexConvert implements Converter<Integer> {
    @Override
    //指定该转换器支持的 Java 类型。在本例中,返回 Integer.class,表示该转换器用于将 Excel 中的数据转换为整数类型。
    public Class supportJavaTypeKey() {
        return Integer.class;
    }

    @Override
    //指定该转换器支持的 Excel 数据类型。在本例中,返回 CellDataTypeEnum.STRING,表示该转换器处理的是字符串类型的 Excel 数据。
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    //将 Excel 中的数据转换为 Java 对象的方法。在本例中,该方法尚未实现,因此返回 null。你需要根据实际需求,实现具体的转换逻辑。
    public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return "男".equals(cellData.getStringValue()) ? 1 : 0;
    }

    @Override
    //将 Java 对象转换为 Excel 数据的方法。
    public CellData convertToExcelData(Integer value, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        if (value == 1) {
            return new CellData("男");
        } else if (value == 0) {
            return new CellData("女");
        }
        // 如果值不是 Integer 或者不是合法的性别值,则抛出异常
        throw new IllegalArgumentException("无效的性别值");
    }
}

  其中第三个和第四个方法代表 java对象与excel表里的相互转换。

*注意包不要导错喔

二、导入功能的实现

1、后端功能实现:

(1)自定义监听器,对下载的excel中的数据进行校验。
public class WebUserListener<T> extends AnalysisEventListener<T> {

    //首先使用 Logger 记录当前读取的数据信息,然后将读取到的数据对象 t 加入到 list 列表中,以备后续的获取
    public final Logger log = LoggerFactory.getLogger(this.getClass());

    public List<T> list = new ArrayList<>();

    /**
     * 该方法在读取到表格中的一条数据时被调用。其中 t 参数表示当前行的数据对象,analysisContext 参数表示解析过程的上下文对象。
     * @param t
     * @param
     */
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        log.info("读取表格[{}]",t);
        list.add(t);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
   //获取数据,供controller层调用
    public List<T> getExcelData(){
        return list;
    }

}
(2)controller层:
@RestController
@RequestMapping("emp/excel")
public class EmpUserImport extends AbstractController {

    @Autowired
    private EmpUserService empUserService;

    @PostMapping("/Import")
    //@RequiresPermissions("emp:user:excelImport")
    public EmpUserEntity redExcel(MultipartFile multipartFile) throws IOException {
        //输入流指向了上传的 Excel 文件
        InputStream inputStream = multipartFile.getInputStream();
        //实例是一个 EasyExcel 的监听器,用于解析 Excel 数据。
        WebUserListener<EmpUserEntity> webListener = new WebUserListener<>();
        //用于处理 Excel 数据的监听器,表示对 Excel 中所有的 sheet 进行解析。
        EasyExcel.read(inputStream,EmpUserEntity.class,webListener).sheet().doRead();
        //获取数据对象列表。
        List<EmpUserEntity> excelData = webListener.getExcelData();
       //将获取到的数据填入数据库
       for (EmpUserEntity user : excelData) {
            empUserService.save(user);
        }
     return excelData ;
    }

2、前端代码

(1)触发按钮
<el-form-item >
        <el-upload
          class="upload-demo"
          :action="excelImports"
          :headers="tokenInfo"
          :on-preview="handlePreview"
          :on-remove="handleRemove"
          :on-success="uploadSuccess"
          multiple
          name="multipartFile"
          :limit="3"
        >
          <el-button  type="success" >点击上传</el-button>
        </el-upload>
    </el-form-item>
(2)接口定义
data () {
      return {
        excelImports: this.$http.adornUrl('/emp/excel/Import'),
        tokenInfo: {
          'token': this.$cookie.get('token')         
        },
      dataForm: {
          deptId: ''  //部门id
        },
    .........
      }
}
(3)方法补充
 uploadSuccess (response, file, fileList) {
      this.getDataList()
      console.log(response)
     },
           
 handleRemove (file, fileList) {
     console.log(file, fileList)
      },
           
  handlePreview (file) {
     console.log(file)
   }

三、导出功能实现

 可实现导出附加功能:

  • 可实现按部门进行导入导出,该页面显示按照部门id进行输入选择,可选择其他条件进行筛选,或自行设置级联选择。
  • 让导出的excel表实现下拉框的选择

1、后端代码

(1)创建实现下拉框选择的注解

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelSelected {

    /**
     * 固定下拉内容
     */
    String[] source() default {};

    /**
     * 设置下拉框的起始行,默认为第二行
     */
    int firstRow() default 1;

    /**
     * 设置下拉框的结束行,默认为最后一行
     */
    int lastRow() default 0x10000;

}

(2)自定义注解使用

	/**
	 * 性别:1:男  0:女
	 */
	@ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class)
	//静态下拉框
	@ExcelSelected(source = {"男","女"})
	private Integer gender;

(2)创建导出工具类

@Slf4j
public class EasyExcelUtils {
    /**
     * 导出单sheet页且sheet页中含有下拉框的excel文件
     *
     * @param response  HttpServletResponse
     * @param fileName  文件名
     * @param sheetName sheet页名
     * @param data      要导出的数据
     */
    public static <T> void writeExcelBySelect(HttpServletResponse response, String fileName, String sheetName, List<T> data) {
        try {
            encodeFileName(response, fileName);
            Map<Integer, ExcelSelected> integerExcelSelectedMap = resolveSelectedAnnotation(IterUtil.getElementType(data));
            EasyExcel.write(response.getOutputStream(), IterUtil.getElementType(data))
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(selectedSheetWriteHandler(integerExcelSelectedMap))
                    .sheet(StringUtils.isEmpty(sheetName) ? "Sheet1" : sheetName)
                    .doWrite(data);
        } catch (IOException e) {
            log.error("导出excel文件异常", e);
        }
    }

    /**
     * 设置文件名
     *
     * @param response HttpServletResponse
     * @param fileName 文件名
     */
    private static void encodeFileName(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", fileName + ".xlsx"));
        response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache");
        response.setHeader(HttpHeaders.PRAGMA, "no-cache");
        response.setDateHeader(HttpHeaders.EXPIRES, -1);
    }

    /**
     * 解析表头类中的下拉注解
     *
     * @param head 表头类
     * @return Map<下拉框列索引, 下拉框内容> map
     */
    private static <T> Map<Integer, ExcelSelected> resolveSelectedAnnotation(Class<T> head) {
        Map<Integer, ExcelSelected> selectedMap = new HashMap<>(16);
        Field[] fields = head.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            ExcelSelected selected = field.getAnnotation(ExcelSelected.class);
            ExcelProperty property = field.getAnnotation(ExcelProperty.class);
            if (selected != null) {
                if (property != null && property.index() >= 0) {
                    selectedMap.put(property.index(), selected);
                }
            }
        }
        return selectedMap;
    }

    /**
     * 为excel创建下拉框
     *
     * @param selectedMap 下拉框配置数据 Map<下拉框列索引, 下拉框内容>
     * @return intercepts handle sheet creation
     */
    private static SheetWriteHandler selectedSheetWriteHandler(Map<Integer, ExcelSelected> selectedMap) {
        return new SheetWriteHandler() {
            @Override
            public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

            }

            @Override
            public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
                //获取正在处理的工作表对象
                Sheet sheet = writeSheetHolder.getSheet();
                //获得一个用于创建数据验证规则的辅助对象,用于创建和配置数据验证规则
                DataValidationHelper helper = sheet.getDataValidationHelper();
                selectedMap.forEach((k, v) -> {//其中 (k, v) 表示 Map 的键和值,在每次迭代时会传入这两个参数。
                    // 获取固定下拉框的内容
                    List<String> source = new ArrayList<>();
                    //如果 ExcelSelected 注解中的 source 数组不为空,则将其转换为 List,并添加到 source 变量中
                    if (v.source().length > 0) {
                        source.addAll(Arrays.asList(v.source()));
                    }

                    //CollUtil.isNotEmpty(source) 是一个对集合 source 进行非空判断的工具方法。
                    if (CollUtil.isNotEmpty(source)) {
                        /**
                         * 代码创建一个 CellRangeAddressList 对象 rangeList,用于定义下拉框的范围。
                         * 其中,v.firstRow() 和 v.lastRow() 分别表示下拉框的起始行和结束行,k 表示下拉框所在的列
                         */
                        CellRangeAddressList rangeList = new CellRangeAddressList(v.firstRow(), v.lastRow(), k, k);
                        //用于定义下拉框的约束。该约束指定下拉框的选项为 source 列表中的内容
                        DataValidationConstraint constraint = helper.createExplicitListConstraint(source.toArray(new String[0]));
                        DataValidation validation = helper.createValidation(constraint, rangeList);
                        //代码设置数据验证规则的错误样式为停止(DataValidation.ErrorStyle.STOP)
                        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
                        //显示错误提示框(validation.setShowErrorBox(true))
                        validation.setShowErrorBox(true);
                        //隐藏下拉箭头(validation.setSuppressDropDownArrow(true))
                        validation.setSuppressDropDownArrow(true);
                        validation.createErrorBox("提示", "请输入下拉选项中的内容");
                        sheet.addValidationData(validation);
                    }
                });
            }
        };
    }
}

 (3)controller层

@GetMapping("/export")
    public void exportData(@RequestParam(required = false) String deptId,//查询条件可不存在
                           HttpServletResponse response) {
        // Data
        QueryWrapper<EmpUserEntity> wrapper = new QueryWrapper<>();
        wrapper.like(StrUtil.isNotBlank(deptId), "dept_id", deptId);

        //将部门id变为部门名称
        List<EmpUserEntity> list = empUserService.list(wrapper);

        EasyExcelUtils.writeExcelBySelect(response, "测试", "用户信息表", list);
}

2、前端代码

(1)按钮展示

<el-form-item style="float:right">
        <el-button type="success" @click="exportUser()">导出</el-button>
      </el-form-item>
      <el-form-item style="float:right">
        <el-input v-model="dataForm.deptId" placeholder="请选择部门进行导出" style="width: 200px" clearable></el-input>
      </el-form-item>

(2)方法定义

// 导出用户,通过blob
exportUser() {
       this.$axios({        
          method: 'get', 
          url: this.$http.adornUrl('/emp/user/export?deptId='+this.dataForm.deptId),
          responseType: 'blob',
        }).then(function (response){
          let blob = new Blob([response.data], { type: 'application/vnd.ms-excel;charset=utf-8' })
                let downloadElement = document.createElement('a');//创建一个 <a> 元素,用于执行文件下载操作。
                let href = window.URL.createObjectURL(blob); //创建下载的链接
                downloadElement.href = href;// 将下载链接指定给 <a> 元素的 href 属性。
                downloadElement.download = 'test.xlsx'; //下载后文件名
                document.body.appendChild(downloadElement);// 将 <a> 元素添加到页面的 <body> 元素中。
                downloadElement.click(); //点击下载
                document.body.removeChild(downloadElement); //下载完成移除元素
                window.URL.revokeObjectURL(href); //释放掉blob对象
        }).catch(function(error){
          this.$message.error(error)
        })		
			},		

3、实物展示

 

----------------------------------------------------------------------------

   本页面的导出下拉框,是固定写入,若需要动态导入,可参考以下大佬文章:

   http://t.csdnimg.cn/zBVD0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1176859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频文件如何添加图片或文字水印?

你是否担心&#xff0c;可能有人盗取你辛苦创作的视频并将其占为己有&#xff0c;从而影响浏览量和收入&#xff1f;或者你只是想为你的视频打造品牌&#xff0c;希望可以产生一些额外的关注和宣传。那么&#xff0c;为你的视频文件添加水印都是很有必要的。 第一部分&#xf…

Linux 内核中根据文件inode号获取其对应的struct inode

文章目录 前言一、简介二、iget_locked2.1 简介2.2 内核中使用2.3 LKM demo 三、ext4_iget3.1 简介3.2 LKM demo 前言 文件inode号和struct inode结构体请参考&#xff1a; Linux文件路径&#xff0c;目录项&#xff0c;inode号关联 Linux文件系统 struct inode 结构体解析 一…

selenium自动化测试入门 —— 键盘鼠标事件ActionChains

在使用 Selenium WebDriver 做自动化测试的时候&#xff0c;会经常模拟鼠标和键盘的一些行为。比如使用鼠标单击、双击、右击、拖拽等动作&#xff1b;或者键盘输入、快捷键使用、组合键使用等模拟键盘的操作。在 WebDeriver 中&#xff0c;有一个专门的类来负责实现这些测试场…

【图像分类】【深度学习】【Pytorch版本】VggNet模型算法详解

【图像分类】【深度学习】【Pytorch版本】VggNet模型算法详解 文章目录 【图像分类】【深度学习】【Pytorch版本】VggNet模型算法详解前言VggNet讲解感受野感受野的计算公式3x3的卷积核的使用VggNet模型结构 VGGnet Pytorch代码完整代码总结 前言 Vgg(Visual Geometry Group)是…

爱思华宝邮件服务器本地文件包含漏洞

爱思华宝邮件服务器本地文件包含漏洞 免责声明漏洞描述漏洞影响漏洞危害网络测绘Fofa: title"Icewarp web klient" 漏洞复现1. 构造poc2. 读取文件 免责声明 仅用于技术交流,目的是向相关安全人员展示漏洞利用方式,以便更好地提高网络安全意识和技术水平。 任何人不…

mysql主从架构

mysql主从架构是一套非常基础的高可用架构&#xff0c;主要依赖复制技术来实现。 1.复制原理 mysql复制功能主要使用三个线程实现&#xff1a; 1.Binary log dump thread&#xff08;二进制日志转储线程&#xff09;:当副本连接时发送二进制日志 2.Replication I/O receiver …

glibc 里的线程 id

这里讲的是通过 pthread_create() 函数返回的线程 id&#xff0c;其实就是 glibc 库里维护的线程id&#xff0c;它跟内核维护的线程 id 不一样&#xff0c;pthread_create() 返回的线程 id 并不一定是唯一的。我们看 pthread_create 函数的源码&#xff0c;它最后返回的线程 id…

【数据分享】2000-2020年我国地级市旅游指标(6项指标/Excel格式/Shp格式)

旅游产业的发展情况是一个城市经济和文化影响力的重要指标&#xff0c;我们在很多研究中都会用到旅游相关的数据&#xff0c;之前我们也分享过一些相关数据&#xff0c;比如1978-2020年的中国旅游统计年鉴和2023年14847条的全国A级景区数据&#xff08;均可查看之前的文章获悉详…

Monarch Mixer:介绍一种性能比Transformer更强的网络架构

六年前&#xff0c;谷歌团队在arXiv上发表了革命性的论文《Attention is all you need》。作为一种优势的机器学习网络架构&#xff0c;Transformer技术迅速席卷全球。Transformer一直是现代基础模型背后的主力架构&#xff0c;并且在不同的应用程序中取得了令人印象深刻的成功…

vue2 集成 - 超图-SuperMap iClient3D for WebGL

1:下载SuperMap iClient3D for WebGL SuperMap iClient3D for WebGL产品包 打开资源目录如下 2:格式化项目中所用的依赖包 开发指南 从超图官网下载SuperMap iClient3D 11i (2023) SP1 for WebGL_CN.zip解压后,将Build目录下的SuperMap3D复制到项目中 \public\static…

Kmeans特征降维方法

文章目录 一、特征降维方法二、数据集介绍三、聚类问题及实现方法四、代码 一、特征降维方法 提取主要特征&#xff0c;忽略次要特征【PCA降维】合并相似特征【特征合并】 PCA主成分提取其实还是会忽略掉一些信息&#xff0c;有时候感觉聚类后的结果并不理想&#xff0c;所以…

非农数据不及预期,美元回落金价触及2000关口

上周五美国非农数据公布&#xff0c;现货黄金短线拉升近16美元&#xff0c;金价突破2000关口最高至2003.55美元/盎司&#xff0c;但随后金价转头回落&#xff0c;最终报收1992.19美元/盎司&#xff0c;涨幅收窄至0.34%。周线级别金价下跌0.61%&#xff0c;金价终止之前连续三周…

助力青少年学习,亚马逊云科技2024年全球人工智能和机器学习奖学金计划正式启动

云未来将以何种方式发展&#xff1f;方向握在意气风发的少年们手中。近日&#xff0c;亚马逊云科技全球人工智能和机器学习&#xff08;AI和ML&#xff09;奖学金计划在中国区的首次颁奖以及2024年启动仪式在北京中学正式举行&#xff0c;有45名学子凭借杰出的学业成绩、对人工…

【Linux】简单部署Yearning并结合内网穿透实现公网访问

文章目录 前言1. Linux 部署Yearning2. 本地访问Yearning3. Linux 安装cpolar4. 配置Yearning公网访问地址5. 公网远程访问Yearning管理界面6. 固定Yearning公网地址 前言 Yearning 简单&#xff0c;高效的MYSQL 审计平台 一款MYSQL SQL语句/查询审计工具&#xff0c;为DBA与开…

Centos7上Python克隆与对网页完整截图

有用的话谢谢点赞~ 安装Python3.11 cd /root wget https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tgz tar -xzf Python-3.11.0.tgz yum -y install gcc zlib zlib-devel libffi libffi-devel yum install readline-devel yum install openssl-devel openssl11 ope…

TCP/IP协议群

TCP/IP协议群 什么是TCP/IP协议群 从字面意义上讲&#xff0c;有人可能会认为 TCP/IP 是指 TCP 和 IP 两种协议。实际生活当中有时也确实就是指这两种协议。然而在很多情况下&#xff0c;它只是利用 IP 进行通信时所必须用到的协议群的统称。具体来说&#xff0c;IP 或 ICMP、…

AI:62-基于深度学习的人体CT影像肺癌的识别与分类

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

MMdetection3.x个人笔记

1.在自定义数据集用训练出的权重进行可视化推理 input(jpg文件) model_config 这两个可以不用加前面的形参 然后用 \ 隔开 写上 --weight xx.pth python demo/image_demo.py data/coco_duck/train2017/10640.jpg work_dirs/solov2_r50_fpn_1x_coco/solov2_r50_fpn_1x_coco…

【MATLAB源码-第68期】基于matlab的802.11b 11Mbps CCK调制解调误码率仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 当涉及802.11b无线局域网&#xff08;Wi-Fi&#xff09;标准时&#xff0c;CCK&#xff08;Complementary Code Keying&#xff09;调制解调技术起着关键作用。下面解释一下CCK在802.11b中的工作原理和特点&#xff1a; 1. 数…