使用VUE+SpringBoot+EasyExcel整合导入导出数据

博客 分享
0 249
优雅殿下
优雅殿下 2022-05-14 13:59:13
悬赏:0 积分 收藏

使用VUE+SpringBoot+EasyExcel 整合导入导出数据

使用VUE+SpringBoot+EasyExcel 整合导入导出数据

创建一个普通的maven项目即可

项目目录结构

image-20220514105517968

1 前端

存放在resources/static 下

index.html

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta http-equiv="X-UA-Compatible" content="IE=edge" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <title>Document</title>    <!-- 开发环境版本,包含了有帮助的命令行警告 -->    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>    <!-- 引入样式 -->    <link      rel="stylesheet"      href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"    />    <!-- 引入组件库 -->    <script src="https://unpkg.com/element-ui/lib/index.js"></script>    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>  </head>  <body>    <div id="app">      <div >        <div >          <el-button            @click="dialogVisible = true"            type="primary"            size="mini"            icon="el-icon-download"          >            导入Excel          </el-button>          <el-dialog            title="数据字典导入"            :visible.sync="dialogVisible"            width="30%"          >            <el-form>              <el-form-item label="请选择Excel文件">                <el-upload                  :auto-upload="true"                  :multiple="false"                  :limit="1"                  :on-exceed="fileUploadExceed"                  :on-success="fileUploadSuccess"                  :on-error="fileUploadError"                  :action="importUrl"                  name="file"                  accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"                >                  <!--accept 只接受某种格式的文件-->                  <el-button size="small" type="primary">点击上传</el-button>                </el-upload>              </el-form-item>            </el-form>            <div slot="footer" >              <el-button @click="dialogVisible = false">取消</el-button>            </div>          </el-dialog>          <!-- 导出 -->          <el-button            @click="exportData"            type="primary"            size="mini"            icon="el-icon-upload2"          >            导出Excel          </el-button>          <!-- 数据展示 -->          <el-table :data="list" stripe >            <el-table-column prop="name" label="姓名" width="180">            </el-table-column>            <el-table-column prop="birthday" label="生日" width="180">            </el-table-column>            <el-table-column prop="salary" label="薪资"> </el-table-column>          </el-table>          <div>            <el-pagination              @size-change="handleSizeChange"              @current-change="handleCurrentChange"              :current-page="pageNum"              :page-sizes="[2, 5, 10,  20]"              :page-size="pageSize"              background              layout="total, sizes, prev, pager, next, jumper"              :total="total"            >            </el-pagination>          </div>        </div>      </div>    </div>  </body>  <script>    new Vue({      el: '#app',      data() {        return {          dialogVisible: false, //文件上传对话框是否显示          list: [], // 字典的数据          importUrl: 'http://localhost:8811/api/excel/import',          pageNum: 1, // 页数          pageSize: 5, // 每页条数          total: 1000,        }      },      created() {        this.showList()      },      methods: {        showList() {          //使用自定义配置          const request = axios.create({            baseURL: 'http://localhost:8811', //url前缀            timeout: 1000, //超时时间            // headers: { token: 'helen123456' }, //携带令牌          })          request            .get('/api/excel/list', {              params: {                pageNum: this.pageNum,                pageSize: this.pageSize,              },            })            .then((res) => {              this.total = res.data.size              this.list = res.data.list              console.log(res)            })        },        // 上传多于一个文件时        fileUploadExceed() {          this.$message.warning('只能选取一个文件')        },		// 导出        exportData() {          window.location.href = 'http://localhost:8811/api/excel/export'        },        //上传成功回调        fileUploadSuccess(response) {          if (response.code === 0) {            this.$message.success('数据导入成功')            this.dialogVisible = false          } else {            this.$message.error(response.message)          }        },        //上传失败回调        fileUploadError(error) {          this.$message.error('数据导入失败')        },        /**         * 用户所选择当前页面展示的数据条数         */        handleSizeChange(val) {          console.log(`每页 ${val} 条`)          this.pageSize = val          this.showList()        },        handleCurrentChange(val) {          console.log(`当前页: ${val}`)          this.pageNum = val          this.showList()        },      },    })  </script></html>

2 数据库

CREATE TABLE `student` (  `name` varchar(255) DEFAULT NULL COMMENT '姓名',  `birthday` datetime DEFAULT NULL COMMENT '生日',  `salary` decimal(10,4) DEFAULT NULL COMMENT '薪资') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3 后端

3.1 contrller

StudentController

@Slf4j@RestController@CrossOrigin@RequestMapping("/api/excel/")public class StudentController {    @Resource    private StudentMapper studentMapper;    @GetMapping("list")    public HashMap<String, Object> list(@RequestParam int pageNum,@RequestParam int pageSize){        // 分页查询        Page<Student> page = new Page<>(pageNum, pageSize);        studentMapper.selectPage(page,null);                // 封装数据        HashMap<String, Object> map = new HashMap<>();        ArrayList<ExcelStudentDTO> excelDictDTOList = new ArrayList<>();        // 转换数据        page.getRecords().forEach(student -> {            ExcelStudentDTO studentDTO = new ExcelStudentDTO();            BeanUtils.copyProperties(student,studentDTO);            excelDictDTOList.add(studentDTO);        });                map.put("list",excelDictDTOList);        map.put("size",page.getTotal());        return map;    }    /**     * 导入     * @param file 文件对象     */    @RequestMapping("import")    @Transactional(rollbackFor = {Exception.class})    public String importData( @RequestParam("file") MultipartFile file){        try {            // 读取文件流            EasyExcel.read                    (file.getInputStream(),// 前端上传的文件                            ExcelStudentDTO.class,// 跟excel对应的实体类                            new ExcelDictDTOListener(studentMapper))// 监听器                     .excelType(ExcelTypeEnum.XLSX)// excel的类型                    .sheet("模板").doRead();            log.info("importData finished");        } catch (IOException e) {           log.info("失败");           e.printStackTrace();        }        return "上传成功";    }    /**     * 导入     */    @GetMapping("export")    public String exportData(HttpServletResponse response){        try {            // 设置响应体内容            response.setContentType("application/vnd.ms-excel");            response.setCharacterEncoding("utf-8");            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系            String fileName = URLEncoder.encode("myStu", "UTF-8").replaceAll("\\+", "%20");            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");            EasyExcel.write(response.getOutputStream()                    ,ExcelStudentDTO.class).sheet().doWrite(studentMapper.selectList(null));        } catch (Exception e) {            e.printStackTrace();        }        return "上传成功";    }}

3.2 mapper

StudentMapper

@Mapperpublic interface StudentMapper extends BaseMapper<Student> {    void insertBatch(List<ExcelStudentDTO> list);}

StudentMapper.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="look.word.mapper.StudentMapper">  <insert id="insertBatch" >    insert into student(name, birthday, salary)    values    <foreach collection="list"   item="item" separator=",">               (        #{item.name} ,        #{item.birthday} ,        #{item.salary}         )    </foreach>  </insert></mapper>

3.3 bean

ExcelStudentDTO

导入数据时 要保证excel中列名和ExcelStudentDTO一致奥

/** * excel对应的实体类 * @author jiejie */@Datapublic class ExcelStudentDTO {	// excel中的列名    @ExcelProperty("姓名")    private String name;    @ExcelProperty("生日")    private Date birthday;    @ExcelProperty("薪资")    private BigDecimal salary;}

Student

/** * 数据库对应的实体类 * @author jiejie */@Data@TableName(value = "student")public class Student {    /**     * 姓名     */    @TableField(value = "name")    private String name;    /**     * 生日     */    @TableField(value = "birthday")    private Date birthday;    /**     * 薪资     */    @TableField(value = "salary")    private BigDecimal salary;    public static final String COL_NAME = "name";    public static final String COL_BIRTHDAY = "birthday";    public static final String COL_SALARY = "salary";}

3.3 listener

官方文档

EasyExcel读取文件需要用到

ExcelDictDTOListener

/** * 监听 * 再读取数据的同时 对数据进行插入操作 * @author : look-word * @date : 2022-05-10 21:35 **/@Slf4j//@AllArgsConstructor //全参@NoArgsConstructor //无参public class ExcelDictDTOListener extends AnalysisEventListener<ExcelStudentDTO> {    /**     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收     */    private static final int BATCH_COUNT = 5;    List<ExcelStudentDTO> list = new ArrayList<ExcelStudentDTO>();    private StudentMapper studentMapper;    //传入mapper对象    public ExcelDictDTOListener(StudentMapper studentMapper) {        this.studentMapper = studentMapper;    }    /**     *遍历每一行的记录     * @param data     * @param context     */    @Override    public void invoke(ExcelStudentDTO data, AnalysisContext context) {        log.info("解析到一条记录: {}", data);        list.add(data);        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM        if (list.size() >= BATCH_COUNT) {            saveData();            // 存储完成清理 list            list.clear();        }    }    /**     * 所有数据解析完成了 都会来调用     */    @Override    public void doAfterAllAnalysed(AnalysisContext context) {        // 这里也要保存数据,确保最后遗留的数据也存储到数据库        saveData();        log.info("所有数据解析完成!");    }    /**     * 加上存储数据库     */    private void saveData() {        log.info("{}条数据,开始存储数据库!", list.size());        studentMapper.insertBatch(list);  //批量插入        log.info("存储数据库成功!");    }}

3.5 config

mybatisPlus分页插件

MybatisPlusConfig

@Configurationpublic class MybatisPlusConfig {    /**     * 新的分页插件,一缓和二缓遵循mybatis的规则,     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)     */    @Bean    public MybatisPlusInterceptor mybatisPlusInterceptor() {        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();        paginationInnerInterceptor.setDbType(DbType.MYSQL);        paginationInnerInterceptor.setOverflow(true);        interceptor.addInnerInterceptor(paginationInnerInterceptor);        return interceptor;    }    @Bean    public ConfigurationCustomizer configurationCustomizer() {        return configuration -> configuration.setUseDeprecatedExecutor(false);    }}

3.6 配置文件

application.yaml

server:  port: 8811spring:  datasource: # mysql数据库连接    type: com.zaxxer.hikari.HikariDataSource    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://localhost:3306/2022_source_springboot?serverTimezone=GMT%2B8&characterEncoding=utf-8    username: root    password: 317311mybatis-plus:  configuration:# sql日志    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  mapper-locations:    - classpath:mapper/*.xml

4 启动测试

启动springboot哦

页面效果图

image-20220514121951441

导出效果

image-20220514122058553

注意

导入数据时 要保证excel中列名和ExcelStudentDTO一致奥

posted @ 2022-05-14 13:06 look-word 阅读(28) 评论(7) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员